Примеры
тестовых заданий

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

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

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

Пример #1

Код показывает незнание основных механизмов, предоставляемых языком программирования. В данном случае необходимо использование конструкции try finally, что позволяет отказаться от второго вызова метода Leave и делат код более логичным, стабильным и читаемым.
Комментировать
var IconCreator: TThread; ... if Assigned(IconCreator) then begin IconCreator.Terminate; IconCreator.WaitFor; IconCreator.Free; IconCreator := nil; end; ...

Пример #2

Вышеприведенный код показывает плохое знание стандартных классов и системных модулей. Во-первых, проверка Assigned, метод Free и операция := nil заменяется на один метод FreeAndNil, во-вторых, при уничтожении объекта потока в его деструкторе, по умолчанию, вызываются Terminate и WaitFor. В итоге, этот участок кода можно заменить на одну строчку кода, что существенно улучшает читаемость кода в целом.
Комментировать
... try SRC.Canvas.Lock; Buffer.Canvas.Lock; ... Buffer.Canvas.Unlock; SRC.Canvas.Unlock; except Result := False; SRC.Canvas.Unlock; Buffer.Canvas.Unlock; end; ...

Пример #3

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

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

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

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

Комментировать
... try J := TJPEGImage.Create; J.LoadFromFile(dir + aFileName); SRC.Assign(J); J.Free; except if Assigned(J) then J.Free; Result := False; end; ...

Пример #4

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

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

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

Комментировать
... if(lkPath[Length(lkPath)] <> '\')then lkPath := lkPath + '\'; ...

Пример #5

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

Пример #6

Наиболее распространённой ошибкой при разработке архитектуры приложения являются перекрестные ссылки на модули (несмотря на то, что с точки зрения языка это является корректным). Например, есть модуль формы ufmMain.pas и модуль потока uThread.pas.
Комментировать
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;

Пример #7

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

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

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

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

Комментировать
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;

Пример #8

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

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

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

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

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

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