Форум: "Прочее";
Текущий архив: 2009.11.01;
Скачать: [xml.tar.bz2];
ВнизКак тестировать мето Assign? Найти похожие ветки
← →
Kolan © (2009-09-02 13:38) [0]Здравствуйте!
Не так давно на форуме обсуждался вопрос о том, как лучше копировать объекты. Многие, и я в том числе, используют ручное копирование полей.
Вопрос:
А можно ли написать тест для методаAssign
так, чтобы если добавил новое поле и забыл дописать присвоение вAssign
, то тест не срабатывал.
Понятно, что тест не должен изменяться при добавлении полей.
← →
DrPass © (2009-09-02 13:57) [1]Для published-полей можно, т.к. они вытягиваются из RTTI, и тест может сам перебрать сколько_их_там_наделано. Для остальных нет.
← →
Kolan © (2009-09-02 14:35) [2]А кто-нибудь тестировал подобные методы? Игорь?
← →
jack128_ (2009-09-02 14:52) [3]я так понимаю ассигн между объектами одного класса идет??? если они умеются стримится - то застримь их и сравни стримы побайтово.
← →
Kolan © (2009-09-02 22:47) [4]А как же ссылки, Джек?
Просто все соглашаются, что ручное сравнение полей — это хорошо, но как не забывать его дописывать?
← →
Игорь Шевченко © (2009-09-03 00:15) [5]
> А кто-нибудь тестировал подобные методы? Игорь?
Я не вижу сложностей в тестировании метода assign, тем более, что при создании объекта его поля обнуляются. Присваивай при создании объекта для теста всем его полям заведомо ненулевые значения, после выполнения assign сравнивай то, что получилось с тем, что должно получиться.
Как не забывать дописывать - например, следить, чтобы адреса полей в тестовом сравнении были непрерывными и адрес последнего поля был на его размер меньше чем адрес начала объекта + InstanceSize
← →
Kolan © (2009-09-03 01:33) [6]А как понять, что поле последнее, если на момент теста его еще нет? Или делать все поля паблишт?
← →
oxffff © (2009-09-03 09:24) [7]
> Игорь Шевченко © (03.09.09 00:15) [5]
>
> > А кто-нибудь тестировал подобные методы? Игорь?
>
>
> Я не вижу сложностей в тестировании метода assign, тем более,
> что при создании объекта его поля обнуляются. Присваивай
> при создании объекта для теста всем его полям заведомо ненулевые
> значения, после выполнения assign сравнивай то, что получилось
> с тем, что должно получиться.
> Как не забывать дописывать - например, следить, чтобы адреса
> полей в тестовом сравнении были непрерывными и адрес последнего
> поля был на его размер меньше чем адрес начала объекта +
> InstanceSize
А как насчет скрытых полей?
1. указателей на VMT реализуемых интерфейсов
2. В новых версиях в конце объекта есть скрытое поле под TMonitor
← →
Омлет (2009-09-03 09:30) [8]Надо писать юнит-тесты, которые же регулярно обновлять при правках класса. Создать себе привычку, так сказать.
← →
Игорь Шевченко © (2009-09-03 09:41) [9]oxffff © (03.09.09 09:24) [7]
Скрытые поля находятся в начале объекта (по меньшей мере на тех версиях Delphi, с которыми я имел дело), так что я не вижу каких-либо затруднений.
← →
oxffff © (2009-09-03 09:49) [10]
> Игорь Шевченко © (03.09.09 09:41) [9]
Это неправда. :)
1. см. функциональность TMonitor
2. где будет поле указатель на VMT ISomeInterface
AClass=Class(Tobject)
a,b:integer;
end;
BClass=Class(AClass,ISomeInterface)
end;
← →
Игорь Шевченко © (2009-09-03 09:57) [11]
> 1. см. функциональность TMonitor
где смотреть ? У себя (D2006) не нашел
> где будет поле указатель на VMT ISomeInterface
по отрицательным смещениям от начала объекта ?
← →
oxffff © (2009-09-03 09:58) [12]
> А можно ли написать тест для метода Assign так, чтобы если
> добавил новое поле и забыл дописать присвоение в Assign,
> то тест не срабатывал.
А компилятор по твоему может понять эквивалентность
A.Field1=B.Field1
A.Field2=B.Field2
и например
CopyMemory(@A.Field1,@B.Field1,Sizeof(A.Field1)+Sizeof(A.Field2))
← →
oxffff © (2009-09-03 10:01) [13]
> Игорь Шевченко © (03.09.09 09:57) [11]
> по отрицательным смещениям от начала объекта ?
А вас на мысли ни на какие не наводит код
procedure TObject.FreeInstance;
begin
CleanupInstance;
_FreeMem(Self);
end;
Почему стоит имеено self, а не self-SizeofHiddenFields.
← →
Игорь Шевченко © (2009-09-03 10:21) [14]oxffff © (03.09.09 10:01) [13]
А тебя не наводит на мысль тот факт, что hiddenfields находятся в vmt, единой для всех экземпляров класса, а не у каждого экземпляра ?
← →
oxffff © (2009-09-03 10:35) [15]
> Игорь Шевченко © (03.09.09 10:21) [14]
Во первых такого поля как hiddenfields нет.
Есть например
vmtSelfPtr = -88;
vmtIntfTable = -84;
vmtAutoTable = -80;
vmtInitTable = -76;
vmtTypeInfo = -72;
vmtFieldTable = -68;
vmtMethodTable = -64;
vmtDynamicTable = -60;
vmtClassName = -56;
vmtInstanceSize = -52;
vmtParent = -48;
Далее черным по белому написано что при освобождении объекта
вызывается деаллокатор c указателем на начало объекта. Ни на -8 как например в динамических массивах, и строках. А именно на начало объекта в привычном понимании.
И самое главное откройте отладчик и посмотрите что да как.
← →
oxffff © (2009-09-03 10:53) [16]
> Игорь Шевченко © (03.09.09 10:21) [14]
> oxffff © (03.09.09 10:01) [13]
>
> А тебя не наводит на мысль тот факт, что hiddenfields находятся
> в vmt, единой для всех экземпляров класса, а не у каждого
> экземпляра ?
Если речь идет о том, что все скрытые поля о которых идет речь находятся в VMT, то это неправда. Поскольку интерфейс реализуется объектом, а не метаклассом.Также как и монитор связан с объектом а не метаклассом.
← →
Игорь Шевченко © (2009-09-03 11:00) [17]oxffff © (03.09.09 10:53) [16]
> Поскольку интерфейс реализуется объектом, а не метаклассом.
Открой отладчик и посмотри. По меньшей мере в D2006 реализация интерфейса одинакова для всех экземпляров класса, а ссылка на эту реализацию находится в vmt.
Что такое TMonitor, я не знаю, в 2006 не нашел
← →
oxffff © (2009-09-03 11:08) [18]
> Игорь Шевченко © (03.09.09 11:00) [17]
> oxffff © (03.09.09 10:53) [16]
>
>
> > Поскольку интерфейс реализуется объектом, а не метаклассом.
>
>
>
> Открой отладчик и посмотри. По меньшей мере в D2006 реализация
> интерфейса одинакова для всех экземпляров класса, а ссылка
> на эту реализацию находится в vmt.
Неправда. см.
Aobject=class
//
end;
Bobject=class(Aobject,Iunknown)
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
showmessage(inttostr(Aobject.InstanceSize));
showmessage(inttostr(Bobject.InstanceSize));
← →
Игорь Шевченко © (2009-09-03 11:14) [19]oxffff © (03.09.09 11:08) [18]
Да, все верно, указатель на экземпляр в таблицу интерфейсов прописывается.
← →
oxffff © (2009-09-03 11:25) [20]
> Игорь Шевченко © (03.09.09 11:14) [19]
В какую такую таблицу интерфейсов прописывается?
Нет ничего такого.
Экземпляр содержит в качестве скрытого поля указатель на VMT.
А сам интерфейс является двойным указателем на VMT.
см.
http://blog.excastle.com/2008/05/10/interfaces-and-reference-equality-beware/
← →
oxffff © (2009-09-03 11:27) [21]
> см.
> http://blog.excastle.com/2008/05/10/interfaces-and-reference-
> equality-beware/
О. Я ему еще и комментарии оставил. Ж)
← →
Игорь Шевченко © (2009-09-03 11:34) [22]
> В какую такую таблицу интерфейсов прописывается?
> Нет ничего такого
Вот в такую:class function TObject.InitInstance(Instance: Pointer): TObject;
{$IFDEF PUREPASCAL}
var
IntfTable: PInterfaceTable;
ClassPtr: TClass;
I: Integer;
begin
FillChar(Instance^, InstanceSize, 0);
PInteger(Instance)^ := Integer(Self);
ClassPtr := Self;
while ClassPtr <> nil do
begin
IntfTable := ClassPtr.GetInterfaceTable;
if IntfTable <> nil then
for I := 0 to IntfTable.EntryCount-1 do
with IntfTable.Entries[I] do
begin
if VTable <> nil then
PInteger(@PChar(Instance)[IOffset])^ := Integer(VTable);
end;
ClassPtr := ClassPtr.ClassParent;
end;
Result := Instance;
end;
{$ELSE}
← →
oxffff © (2009-09-03 11:52) [23]
> Игорь Шевченко © (03.09.09 11:34) [22]
>
> > В какую такую таблицу интерфейсов прописывается?
> > Нет ничего такого
Это не таблица, поскольку все записи по PInteger(@PChar(Instance)[IOffset])^ не обязательно будет составлять непрерывный участок памяти в экземпляре.
Только это не таблица интерфейсов, поскольку ее элементы не являются интерфейсами, а являются указателями на VMT интерфейса. А интерфейс это двойной указатель на VMT.
← →
Cobalt © (2009-09-03 12:03) [24]Насчет полей - это все, конечно, интересно. VMT, все дела...
НО.
если при Assign надо будет еще и какие-то действия производить над полями, то все-равно придется проверять лично.
Так что от упущения спасет только технология: добавили поле - проверили все методы, что нужны.
← →
Kolan © (2009-09-03 12:24) [25]Cobalt, но как не забыть?
← →
brother © (2009-09-03 12:27) [26]> Cobalt, но как не забыть?
записывай на бумажке...
← →
Kolan © (2009-09-03 15:24) [27]Еще вопрос, кстати.
Большой ли грех не удалять объекты в тестах?
← →
Kolan © (2009-09-04 18:27) [28]Если честно, я так и не разобрался как сделать тест сабжа.
Приведу пример.
Есть кредит:TCredit = class(TSimpleIDObject)
private
FCreditPeriod: Integer;
FCreditSize: Currency;
FYearPercent: Integer;
FPaymentDay: Integer;
FContractNumber: string;
FContractDate: TDateTime;
FPaymentsSumm: Currency;
FDelayCount: Integer;
FActiveDelaysCount: Integer;
FFullMustBePaymentSum: Currency;
public
procedure Assign(ACredit: TCredit);
function IsPaiedOff: Boolean;
function LeftPaymentDiffrence: Currency;
property ContractNumber: string read FContractNumber write FContractNumber;
property ContractDate: TDateTime read FContractDate write FContractDate;
property PaymentDay: Integer read FPaymentDay write FPaymentDay;
property CreditSize: Currency read FCreditSize write FCreditSize;
property CreditPeriod: Integer read FCreditPeriod write FCreditPeriod;
property YearPercent: Integer read FYearPercent write FYearPercent;
property PaymentsSumm: Currency read FPaymentsSumm write FPaymentsSumm;
property FullMustBePaymentSum: Currency read FFullMustBePaymentSum write FFullMustBePaymentSum;
property DelayCount: Integer read FDelayCount write FDelayCount;
property ActiveDelaysCount: Integer read FActiveDelaysCount write FActiveDelaysCount;
end;
Есть метод Assign:procedure TCredit.Assign(ACredit: TCredit);
begin
inherited Assign(ACredit);
if Assigned(ACredit) then
begin
FCreditPeriod := ACredit.CreditPeriod;
FCreditSize := ACredit.CreditSize;
FYearPercent := ACredit.YearPercent;
FPaymentDay := ACredit.PaymentDay;
FContractNumber := ACredit.ContractNumber;
FContractDate := ACredit.ContractDate;
FPaymentsSumm := ACredit.PaymentsSumm;
FFullMustBePaymentSum := ACredit.FullMustBePaymentSum;
FDelayCount := ACredit.DelayCount;
FActiveDelaysCount := ACredit.ActiveDelaysCount;
end;
end;
Есть тест:procedure TestTCredit.TestAssign;
var
ACredit: TCredit;
begin
ACredit := TCredit.Create;
try
ACredit.ID := 1;
FCredit.Assign(ACredit);
Assert(FCredit.ID = 1);
finally
ACredit.Free;
end;
end;
Как надо переделать тест, чтобы не забывать про добавление полей?
← →
oxffff © (2009-09-04 19:01) [29]
> Kolan © (04.09.09 18:27) [28
1. Delphi 2010 + RTTI в руки.
2. Каждое поле делаешь отдельным объектом или вариантой записью и делаешь примерно так
Пример для варианта
TSomeValueStuff=record
name:string;
value:variant;
end;
TCompositeObject = class( ...)
ItemsContainer:TLIST<TSomeValueStuff>;
constructor create(Names:arrray of string);
procedure assign(source:TCompositeObject);
end;
TCredit =class(TCompositeObject)
constructor create();
end;
constructor TCompositeObject.create(Names:arrray of string);
var name:string;
SomeValueStuff:TSomeValueStuff;
begin
ItemsContainer:= Tlist<...>.create;
for name in Names do
begin
SomeValueStuff.name=name;
ItemsContainer.add(SomeValueStuff);
end;
end;
constructor TCredit.create();
begin
inherited create("FCreditPeriod","FCreditSize",....);
end;
procedure assign(source:TCompositeObject);
var Value:TSomeValueStuff;
Newvalue:TSomeValueStuff;
begin
if ClassType=source.classtype then
begin
for value in source.ItemsContainer do
ItemsContainer[ItemsContainer.indexof(value)]:= value;
end;
end;
+декструктор + ICompare для LIST<> сделаешь сам.
← →
oxffff © (2009-09-04 19:02) [30]
> oxffff © (04.09.09 19:01) [29]
Это псевдокод.
← →
Kolan © (2009-09-04 19:22) [31]1. А для Д 2009?
А обращаться что по строке? Я хочу подсказок в ИДЕ и чтобы у других людей при виде этого не съехала крыша.
← →
oxffff © (2009-09-04 19:25) [32]
> Kolan © (04.09.09 19:22) [31]
> 1. А для Д 2009?
Вариант 2
← →
oxffff © (2009-09-04 19:27) [33]
> А обращаться что по строке? Я хочу подсказок в ИДЕ и чтобы
> у других людей при виде этого не съехала крыша.
Напиши дополнение к IDE через ToolAPi. :)
← →
oxffff © (2009-09-04 19:29) [34]
> Kolan © (04.09.09 19:22) [31]
> 1. А для Д 2009?
>
> А обращаться что по строке? Я хочу подсказок в ИДЕ и
Может дописать алиасы к полям через.
property ContractNumber: string read FContractNumber write FContractNumber;
property ContractDate: TDateTime read FContractDate write FContractDate;
property PaymentDay: Integer read FPaymentDay write FPaymentDay;
property CreditSize: Currency read FCreditSize write FCreditSize
Однако assign у тебя будет один на любые композиции частей.
← →
Игорь Шевченко © (2009-09-04 20:01) [35]Kolan © (04.09.09 18:27) [28]
Как я делаю подобные тесты:
procedure TestTCredit.TestAssign;
var
ACredit: TCredit;
begin
ACredit := TCredit.Create;
try
ACredit.Assign(FCredit);
Assert(ACredit.IsEqual(FCredit));
finally
ACredit.Free;
end;
end;
почему изменен порядок в assign - для единообразия с TPersistent
← →
Kolan © (2009-09-04 22:11) [36]Хороший тест, понятный. Только, вот...
А что вIsEqual
? И как тестировать его?
--
Лично для меня методыIsEqual
иIsEqual
— почти одно и тоже.
← →
Kolan © (2009-09-04 22:12) [37]oxffff, это решения крутые, да, но я не пойду на такое усложнение ради теста. Проще и правда «не забывать».
← →
Игорь Шевченко © (2009-09-04 22:16) [38]
> И как тестировать его?
тем же тестом :) можно написать отдельный ради чистоты теории, но зачем ?
← →
Kolan © (2009-09-04 22:25) [39]Что-то я туплю.
Если я добавил поле, но забыл его присвоить (или сравнить) в IsEqual и Assign, то тест сработает, но ошибку не покажет.
Так? Тогда что проверяет этот тест? — Ничего полезного, имхо.
← →
Игорь Шевченко © (2009-09-04 22:29) [40]Kolan © (04.09.09 22:25) [39]
Если ты забыл добавить в оба метода - кто тебе после этого доктор - только паталогоанатом. Я таким образом ловил свои ошибки.
Причем, желательно чтобы свойства образца (FCredit в твоем примере) были заполнены ненулевыми значениями.
Страницы: 1 2 вся ветка
Форум: "Прочее";
Текущий архив: 2009.11.01;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.007 c