Примеры тестовых работ

Любая из вакансий в компании Auslogics предполагает выполнение тестового задания. Это обязательная часть процесса отбора соискателей.

Тестовые задания разрабатываются для каждой отдельной должности и учитывают весь набор компетенций, которыми должен обладать сотрудник, который в будущем займет эту должность.
Вопросы и задачи тестов изменяются практически с каждой новой вакансией.

О критериях, на которые мы обращаем внимание при проверке работ детально написано в разделе «Карьера у нас». К сожалению, мы не можем разобрать ошибки тестовых заданий для всех должностей, имеющихся в компании, но для примера мы решили привести и прокомментировать ряд выполненных соискателями тестовых работ на должность "Программист Delphi".

Пример №1

var
	CS: TCriticalSection;
...
	CS.Enter;
	if (Dir <> CurrentDir) then
	begin
		CS.Leave;
		Exit;
	end;
	CS.Leave;
...

Код показывает незнание основных механизмов, предоставляемых языком программирования. В данном случае необходимо использование конструкции try finally, что позволяет отказаться от второго вызова метода Leave и делат код более логичным, стабильным и читаемым.

Комментировать

Пример №2

var
	IconCreator: TThread;
...
	if Assigned(IconCreator) then
	begin
		IconCreator.Terminate;
		IconCreator.WaitFor;
		IconCreator.Free;
		IconCreator := nil;
	end;
...

Вышеприведенный код показывает плохое знание стандартных классов и системных модулей. Во-первых, проверка Assigned, метод Free и операция := nil заменяется на один метод FreeAndNil, во-вторых, при уничтожении объекта потока в его деструкторе, по умолчанию, вызываются Terminate и WaitFor. В итоге, этот участок кода можно заменить на одну строчку кода, что существенно улучшает читаемость кода в целом.

Комментировать

Пример №3

...
	try
		SRC.Canvas.Lock;
		Buffer.Canvas.Lock;
		...
		Buffer.Canvas.Unlock;
		SRC.Canvas.Unlock;
	except
		Result := False;
		SRC.Canvas.Unlock;
		Buffer.Canvas.Unlock;
	end;
...

Данный код показывает незнание основных механизмов, предоставляемых языком программирования, и содержит две грубые ошибки при использовании методов блокировки ресурсов для доступа.

- Повторный вызов методов Unlock в блоке except лишний и ненужный, т.к. достаточно использовать конструкции try finally. Это позволяет избежать дублирования и сделать код более читабельным и логичным.

- Отсутствие операторов try finally между вызовами Lock и Unlock может привести к тому, что ресурс останется заблокированным или будет вызван Unlock на незаблокированном ресурсе.

- Вызов Unlock в блоке except может выполниться до того, как будет вызван Lock для этого ресурса.

Комментировать

Пример №4

...
	try
		J := TJPEGImage.Create;
		J.LoadFromFile(dir + aFileName);
		SRC.Assign(J);
		J.Free;
	except
		if Assigned(J) then
		J.Free;
		Result := False;
	end;
...

Данный код содержит несколько грубых ошибок, связанных с непониманием основных механизмов языка разработки и используемых в нем принципов освобождения ресурсов:

- При возникновении ошибки в конструкторе TJPEGImage в секции exception значение переменной J будет неопределенно, т.е. она может не равняться nil, что приведет к вызову метода Free для несуществующего объекта и как следствие к ошибке access violation.

- Логика данного участка кода предполагает использование блока try finally для контроля за уничтожением объекта TJPEGImage. Использование try finally позволит избежать ненужных проверок на существование объекта и дублирование метода Free.

Комментировать

Пример №5

...
	if(lkPath[Length(lkPath)] <> '\')then
		lkPath := lkPath + '\';
...

Код показывает плохое знание системных модулей, т.к. для данной операции есть стандартная функция IncludeTrailingPathDelimiter. Также приведенный пример кода не защищен от ошибки в случае строки с нулевой длиной.

Комментировать

Пример №6

Наиболее распространённой ошибкой при разработке архитектуры приложения являются перекрестные ссылки на модули (несмотря на то, что с точки зрения языка это является корректным). Например, есть модуль формы ufmMain.pas и модуль потока uThread.pas:

unit ufmMain;
interface
uses
	uThread;
...
implementation
...
end.

unit uThread;
interface
...
implementation
uses
	ufmMain;
...
end.

Комментировать

Пример №7

const
	ThreadSuccess= 9;
	ThreadError=  -1;
type
	TThumbsThread = class(TThread)
	protected
		procedure Execute;  override;
	end;

procedure TThumbsThread.Execute;
begin
	ReturnValue:=0;
	try
		...
		ReturnValue := ThreadSuccess;
	except
		ReturnValue := ThreadError;
	end;
end;

procedure TMainForm.RedrawThumbnails();
begin
	...
	if Assigned(ThumbsThread) then
	begin
		ThumbsThread.Terminate;
		while (ThumbsThread.WaitFor= 0) do
			Application.ProcessMessages;
		ThumbsThread:= nil;
	end;
	ThumbsThread:= TThumbsThread.Create(true);
	ThumbsThread.FreeOnTerminate:= false;
		...
	ThumbsThread.Resume;
	...
end;

Данный код содержит несколько ошибок работы с потоками, что свидетельствует о неполных знаниях по данной теме:

- Цикл ожидания с проверкой результата выполнения функции WaitFor нелогичен и, в случае ошибки выполнения потока, приведет просто к зависанию приложения, т.к. функция WaitFor блокирует выполнение, ожидает завершение потока и возвращает результат работы потока. Т.е. значение, возвращаемое функцией WaitFor, с течением времени не меняется. К тому же, сам цикл в данном случае нелогичен, т.к. он не выполняется (функция блокирует выполнение до завершения потока).

- Непонятно и нелогично использование свойства потока ReturnValue, т.к. оно фактически нигде не используется (только для цикла с проверкой WaitFor, о чем написано в предыдущем пункте).

- Объекты типа TThumbsThread никогда не освобождаются, при этом создаются в больших количествах, что приводит к утечкам памяти.

Комментировать

Пример №8

type
	TFindPictures = class(TThread)
	private
	procedure AddPicture(Name : string; aIndex : Integer);
	protected
		procedure Execute; override;
	end;

procedure TFindPictures.Execute;
var
	SearchRec : TSearchRec;
	ThumbImage : TGPImage;
	ImageIndex : Integer;
begin
	FindFirst(Directory + '\*.*',  faAnyFile, SearchRec);
	if SearchRec.Name <> '' then
	begin
	repeat
		if isPicture(SearchRec.Name) then
		begin
		ThumbImage := CreateThumb(IncludeTrailingPathDelimiter(Directory)
					+ SearchRec.Name);
		ImageIndex := AddThumbInList(ThumbImage);
		AddPicture(SearchRec.Name, ImageIndex);
		end;
	until ((FindNext(SearchRec) <> 0) or Terminated)
	end;
	FindClose(SearchRec);
end;

procedure TFindPictures.AddPicture(Name: string; aIndex : Integer);
var
	Picture : TListItem;
begin
	Picture := ListView.Items.Add;
	Picture.Caption := Name;
	Picture.ImageIndex := aIndex;
end;

type
	TListViewThread = class(TListView)
	protected
		FindPicture : TFindPictures;
	public
	procedure DirectoryChange(var Msg : TMessage); message WM_DIRECTORY_CHANGE;
	end;

procedure TListViewThread.DirectoryChange(var Msg: TMessage);
var
	CurrentDirectory : string;
begin
	CurrentDirectory := PChar(Pointer(Msg.LParam));
	Self.Items.Clear;
	if Assigned(FindPicture) then
			FindPicture.Terminate;
	FindPicture := TFindPictures.Create(CurrentDirectory, Self);
	FindPicture.Resume;
end;

Данный код содержит несколько ошибок работы с потоками, что свидетельствует о неполных знаниях в данной теме:

- Объекты типа TFindPictures никогда не освобождаются, но создаются при каждой смене директории, что приводит к утечкам памяти.

- При завершении работы приложения не выполняется остановка потока, что может привести к очень долгой выгрузке приложения из памяти, в случае большого количества графических файлов в папке (приложение не завершится, пока не будут загружены все файлы из папки).

- При прерывании потока нет ожидания его завершения, поэтому, в случае переключения на другую папку, когда чтение файлов еще не завершено, поток будет еще выполнятся, и добавит в список отображения последний обрабатываемый файл.

- Работа с GUI компонентами из потока не синхронизирована, что может приводить к различным ошибкам в работе приложения.

Комментировать