DOM test suite enhancements:

* Do not convert tests which request implementation attribute 'signed'='true'.
  Such tests aren't applicable to our unsigned DOM, they only cause compiler warnings
  and noise in the test report.
+ Support for default properties (obj.item(x) -> obj[x]).
+ Support black-listing of testcases. Some of them (in HTML testsuite) are easier to
  rewrite by hand than to convert.
+ Support adding certain units to 'uses' clause (e.g. HTML suite must use dom_html).

git-svn-id: trunk@13172 -
This commit is contained in:
sergei 2009-05-17 22:56:51 +00:00
parent 501f20c546
commit 6049600ccb
2 changed files with 105 additions and 32 deletions

View File

@ -5,20 +5,32 @@
This is used by testgen program to convert w3.org XML test descriptions into This is used by testgen program to convert w3.org XML test descriptions into
fpcUnit-compatible Pascal code. fpcUnit-compatible Pascal code.
--> -->
<!DOCTYPE api [ <!DOCTYPE test-data [
<!ELEMENT test-data (blacklist*, uses*, api)>
<!ELEMENT api (item)+ > <!ELEMENT api (item)+ >
<!ELEMENT item (arg*)> <!ELEMENT item (arg*)>
<!ELEMENT arg (#PCDATA)> <!ELEMENT arg (#PCDATA)>
<!ELEMENT blacklist (#PCDATA)>
<!ELEMENT uses EMPTY>
<!ATTLIST item <!ATTLIST item
id ID #REQUIRED id ID #REQUIRED
type (func|prop|method) "func" type (func|prop|method|defprop) "func"
result CDATA #IMPLIED result CDATA #IMPLIED
objtype CDATA #IMPLIED objtype CDATA #IMPLIED
rename CDATA #IMPLIED rename CDATA #IMPLIED
gc (yes|no) #IMPLIED> gc (yes|no) #IMPLIED>
<!ATTLIST arg <!ATTLIST arg
type CDATA #IMPLIED> type CDATA #IMPLIED>
]> <!ATTLIST uses
pattern CDATA #REQUIRED
unit CDATA #REQUIRED>
]>
<test-data>
<blacklist>HTMLCollection04</blacklist>
<blacklist>HTMLTableElement21</blacklist>
<blacklist>HTMLTableElement24</blacklist>
<uses pattern="/html" unit="dom_html"/>
<uses pattern="/xpath" unit="xpath"/>
<api> <api>
<item id="createDocumentFragment"/> <item id="createDocumentFragment"/>
<item id="createTextNode"> <item id="createTextNode">
@ -77,11 +89,9 @@
<item id="publicId" type="prop"/> <!-- settable for DOM lvl 3 LSInput --> <item id="publicId" type="prop"/> <!-- settable for DOM lvl 3 LSInput -->
<item id="systemId" type="prop"/> <item id="systemId" type="prop"/>
<item id="notationName"/> <item id="notationName"/>
<!-- Handled separately because our DOM has it as a default property, not a function <item id="item" result="Node" type="defprop">
<item id="item" result="Node">
<arg>index</arg> <arg>index</arg>
</item> </item>
-->
<item id="getNamedItem" result="Node"> <item id="getNamedItem" result="Node">
<arg>name</arg> <arg>name</arg>
</item> </item>
@ -146,7 +156,7 @@
<item id="getElementsByTagName" gc="yes"> <item id="getElementsByTagName" gc="yes">
<arg>tagname</arg> <arg>tagname</arg>
</item> </item>
<item id="childNodes" gc="yes"/> <item id="childNodes"/>
<item id="value" type="prop"/> <item id="value" type="prop"/>
<item id="nodeValue" type="prop"/> <item id="nodeValue" type="prop"/>
@ -692,4 +702,5 @@
<item id="HTMLTitleElement.text" type="prop"/> <item id="HTMLTitleElement.text" type="prop"/>
</api> </api>
</test-data>

View File

@ -26,6 +26,7 @@ var
forced: Boolean = False; forced: Boolean = False;
TestCount: Integer = 0; TestCount: Integer = 0;
FailCount: Integer = 0; FailCount: Integer = 0;
IgnoreCount: Integer = 0;
function PascalType(const s: WideString): string; function PascalType(const s: WideString): string;
begin begin
@ -196,7 +197,7 @@ var
child, subchild: TDOMNode; child, subchild: TDOMNode;
n: DOMString; n: DOMString;
SuccessVarFlag: Boolean; SuccessVarFlag: Boolean;
FailFlag: Boolean; FailFlag, IgnoreFlag: Boolean;
Inits, VarTypes: TStringList; Inits, VarTypes: TStringList;
function TypeOfVar(const varname: string): string; function TypeOfVar(const varname: string): string;
@ -266,15 +267,20 @@ begin
Result := getobj(e) + '.' + fixname(e) + ' := ' + ReplaceQuotes(e['value']) + ';'; Result := getobj(e) + '.' + fixname(e) + ' := ' + ReplaceQuotes(e['value']) + ';';
end; end;
function func_call(e: TDOMElement; args: TDOMNodeList; const rsltType: string=''): string; function func_call(e: TDOMElement; args: TDOMNodeList; const rsltType: string=''; IsDefProp: Boolean=False): string;
begin begin
if (rsltType <> '') and (TypeOfVar(e['var']) <> rsltType) then if (rsltType <> '') and (TypeOfVar(e['var']) <> rsltType) then
Result := rsltType + '(' + e['var'] + ')' Result := rsltType + '(' + e['var'] + ')'
else else
Result := e['var']; Result := e['var'];
Result := Result + ' := ' + getobj(e) + '.' + fixname(e); if IsDefProp then
if args.Length > 0 then Result := Result + ' := ' + getobj(e) + '[' + argstring(e, args) + ']'
Result := Result + '(' + argstring(e, args) + ')'; else
begin
Result := Result + ' := ' + getobj(e) + '.' + fixname(e);
if args.Length > 0 then
Result := Result + '(' + argstring(e, args) + ')';
end;
Result := Result + ';'; Result := Result + ';';
end; end;
@ -358,7 +364,7 @@ begin
cond := PascalType(apinode['result']) cond := PascalType(apinode['result'])
else else
cond := ''; cond := '';
rslt.Add(indent + func_call(node, arglist, cond)); rslt.Add(indent + func_call(node, arglist, cond, apinode['type']='defprop'));
if apinode['gc'] = 'yes' then if apinode['gc'] = 'yes' then
rslt.Add(indent + 'GC(' + node['var'] + ');'); rslt.Add(indent + 'GC(' + node['var'] + ');');
end; end;
@ -366,11 +372,7 @@ begin
end; end;
// now, various hacks and workarounds // now, various hacks and workarounds
if s = 'length' then
// TODO: modify DOM to expose item() as function
if s = 'item' then
rslt.Add(indent + 'TDOMNode('+node['var'] + ') := ' + node['obj'] + '['+node['index']+'];')
else if s = 'length' then
begin begin
if node['interface'] = 'DOMString' then if node['interface'] = 'DOMString' then
rslt.Add(indent + node['var'] + ' := system.length(' + node['obj'] + ');') rslt.Add(indent + node['var'] + ' := system.length(' + node['obj'] + ');')
@ -471,7 +473,11 @@ begin
else if n = 'load' then else if n = 'load' then
rslt.Add(indent + 'Load('+node['var']+', '''+ node['href']+''');') rslt.Add(indent + 'Load('+node['var']+', '''+ node['href']+''');')
else if s = 'implementationAttribute' then else if s = 'implementationAttribute' then
begin
if (node['name']='signed') and (node['value']='true') then
IgnoreFlag := True;
rslt.Add(indent + s + '[''' + node['name'] + '''] := ' + node['value'] + ';') rslt.Add(indent + s + '[''' + node['name'] + '''] := ' + node['value'] + ';')
end
else if s = 'createXPathEvaluator' then else if s = 'createXPathEvaluator' then
rslt.Add(indent + node['var'] + ' := CreateXPathEvaluator(' + node['document'] + ');') rslt.Add(indent + node['var'] + ' := CreateXPathEvaluator(' + node['document'] + ');')
else if s = 'comment' then else if s = 'comment' then
@ -746,6 +752,7 @@ end;
begin begin
SuccessVarFlag := False; SuccessVarFlag := False;
FailFlag := False; FailFlag := False;
IgnoreFlag := False;
VarTypes := TStringList.Create; VarTypes := TStringList.Create;
Inits := TStringList.Create; Inits := TStringList.Create;
ConvertVars; ConvertVars;
@ -763,6 +770,11 @@ begin
rslt.Clear; rslt.Clear;
Inc(FailCount); Inc(FailCount);
end; end;
if IgnoreFlag then
begin
rslt.Clear;
Inc(IgnoreCount);
end;
end; end;
// Intercepting validation errors while loading API // Intercepting validation errors while loading API
@ -777,6 +789,19 @@ begin
raise E; raise E;
end; end;
function IsBlacklisted(const s: string; const list: array of string): Boolean;
var
I: Integer;
begin
Result := True;
for I := Low(list) to High(list) do
begin
if s = list[I] then
Exit;
end;
Result := False;
end;
const const
UnitHeader = UnitHeader =
@ -784,18 +809,18 @@ const
' This Pascal source file was generated by testgen program'#10 + ' This Pascal source file was generated by testgen program'#10 +
' and is a derived work from the source document.'#10 + ' and is a derived work from the source document.'#10 +
' The source document contained the following notice:'#10+ ' The source document contained the following notice:'#10+
'%s}'#10+ '%0:s}'#10+
'unit %s;'#10 + 'unit %1:s;'#10 +
'{$mode objfpc}{$h+}'#10 + '{$mode objfpc}{$h+}'#10 +
'{$notes off}'#10 + '{$notes off}'#10 +
'{$codepage utf8}'#10 + '{$codepage utf8}'#10 +
'interface'#10 + 'interface'#10 +
#10 + #10 +
'uses'#10 + 'uses'#10 +
' SysUtils, Classes, DOM, xmlread, fpcunit, contnrs, domunit, testregistry;'#10 + ' SysUtils, Classes, DOM, xmlread, fpcunit, contnrs, domunit, testregistry%3:s;'#10 +
#10 + #10 +
'type'#10 + 'type'#10 +
' %s = class(TDOMTestBase)'#10 + ' %2:s = class(TDOMTestBase)'#10 +
' protected'#10 + ' protected'#10 +
' function GetTestFilesURI: string; override;'#10 + ' function GetTestFilesURI: string; override;'#10 +
' published'#10; ' published'#10;
@ -810,8 +835,9 @@ var
sl, all, impl: TStringList; sl, all, impl: TStringList;
Pars: TDOMParser; Pars: TDOMParser;
eh: TErrHandler; eh: TErrHandler;
class_name, unit_name, notice: string; class_name, unit_name, notice, casename, add_units: string;
comment: TDOMNode; comment: TDOMNode;
blacklist: array of string;
begin begin
Pars := TDOMParser.Create; Pars := TDOMParser.Create;
eh := TErrHandler.Create; eh := TErrHandler.Create;
@ -820,6 +846,16 @@ begin
// API database must be loaded in validating mode // API database must be loaded in validating mode
Pars.ParseURI('file:api.xml', api); Pars.ParseURI('file:api.xml', api);
// Prepare the array of blacklisted test names
testlist := api.GetElementsByTagName('blacklist');
try
SetLength(blacklist, testlist.length);
for I := 0 to testlist.length-1 do
blacklist[I] := testlist[I].TextContent;
finally
testlist.Free;
end;
sl := TStringList.Create; sl := TStringList.Create;
all := TStringList.Create; all := TStringList.Create;
impl := TStringList.Create; impl := TStringList.Create;
@ -841,10 +877,24 @@ begin
comment := comment.nextSibling; comment := comment.nextSibling;
end; end;
// Check if we need the additional units to use
add_units := '';
testlist := api.GetElementsByTagName('uses');
try
for I := 0 to testlist.Length-1 do
begin
root := TDOMElement(testlist[I]);
if Pos(root['pattern'], BaseURI) <> 0 then
add_units := add_units + ', ' + root['unit'];
end;
finally
testlist.Free;
end;
unit_name := ChangeFileExt(ExtractFileName(UnitFileName), ''); unit_name := ChangeFileExt(ExtractFileName(UnitFileName), '');
class_name := 'TTest' + UpperCase(unit_name[1]) + copy(unit_name, 2, MaxInt); class_name := 'TTest' + UpperCase(unit_name[1]) + copy(unit_name, 2, MaxInt);
// provide unit header // provide unit header
all.Text := Format(UnitHeader, [notice, unit_name, class_name]); all.Text := Format(UnitHeader, [notice, unit_name, class_name, add_units]);
// emit the 'GetPathToModuleFiles' function body // emit the 'GetPathToModuleFiles' function body
impl.Add('implementation'); impl.Add('implementation');
impl.Add(''); impl.Add('');
@ -862,23 +912,30 @@ begin
begin begin
href := TDOMElement(testlist[I])['href']; href := TDOMElement(testlist[I])['href'];
ResolveRelativeURI(BaseURI, href, testuri); ResolveRelativeURI(BaseURI, href, testuri);
Pars.ParseURI(testuri, testdoc); Pars.ParseURI(testuri, testdoc);
try try
sl.Clear; sl.Clear;
root := testdoc.DocumentElement; root := testdoc.DocumentElement;
// fix clash with local vars having the same name // fix clash with local vars having the same name
if root['name'] = 'attrname' then casename := root['name'];
root['name'] := 'attr_name'; if casename = 'attrname' then
sl.Add('procedure ' + class_name + '.' + root['name'] + ';'); casename := 'attr_name';
if IsBlacklisted(casename, blacklist) then
begin
writeln('Test case "', casename, '" is blacklisted, skipping');
Continue;
end;
sl.Add('procedure ' + class_name + '.' + casename + ';');
try try
ConvertTest(root, sl); ConvertTest(root, sl);
except except
Writeln('An exception occured while converting '+root['name']); Writeln('An exception occured while converting ', casename);
raise; raise;
end; end;
if sl.Count > 0 then if sl.Count > 0 then
begin begin
all.add(' procedure '+root['name']+';'); all.add(' procedure '+casename+';');
impl.AddStrings(sl) impl.AddStrings(sl)
end; end;
finally finally
@ -912,7 +969,7 @@ var
I: Integer; I: Integer;
begin begin
writeln('testgen - w3.org DOM test suite to Pascal converter'); writeln('testgen - w3.org DOM test suite to Object Pascal converter');
writeln('Copyright (c) 2008 by Sergei Gorelkin'); writeln('Copyright (c) 2008 by Sergei Gorelkin');
if ParamCount < 2 then if ParamCount < 2 then
@ -941,7 +998,7 @@ begin
ConvertSuite(FilenameToURI(SuiteName), OutputUnit); ConvertSuite(FilenameToURI(SuiteName), OutputUnit);
writeln(testcount - FailCount, ' tests converted successfully'); writeln(testcount - FailCount - IgnoreCount, ' tests converted successfully');
if FailCount > 0 then if FailCount > 0 then
begin begin
writeln(FailCount, ' tests contain tags that are not supported yet'); writeln(FailCount, ' tests contain tags that are not supported yet');
@ -953,5 +1010,10 @@ begin
else else
writeln('These tests were skipped'); writeln('These tests were skipped');
end; end;
if IgnoreCount > 0 then
begin
writeln(IgnoreCount, ' tests were skipped because they are not');
writeln(' applicable to our DOM implementation.');
end;
end. end.