You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

collapsed-conditional-borders_test-generator.py 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. #!/usr/bin/python
  2. #
  3. # Licensed to the Apache Software Foundation (ASF) under one or more
  4. # contributor license agreements. See the NOTICE file distributed with
  5. # this work for additional information regarding copyright ownership.
  6. # The ASF licenses this file to You under the Apache License, Version 2.0
  7. # (the "License"); you may not use this file except in compliance with
  8. # the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. #
  18. # $Id$
  19. """A testcase generator for conditional borders and the collapsing border model, in the FO
  20. tree.
  21. Computes all the possible combinations of borders (retained/discarded, with/without header
  22. and footer, etc.) and generates the corresponding tables together with the expected
  23. resolved borders.
  24. The two functions to call are generateTestCases and generateTestCasesHeaderFooter; each of
  25. them returns a complete FO file on stdout and a table of resolved borders on stderr, to be
  26. included in the Java test case. This is all a bit rough be enough to get the testcases
  27. generated.
  28. Type definitions:
  29. border specification:
  30. {'length': <string, e.g. '4pt'>,
  31. 'cond': <'retain' or 'discard'>
  32. 'color': <string, e.g. 'black'>
  33. }
  34. """
  35. import sys;
  36. import copy;
  37. fo_table = 0
  38. fo_column = 1
  39. fo_body = 2
  40. fo_row = 3
  41. fo_cell = 4
  42. def printFOStart():
  43. print '<?xml version="1.0" standalone="no"?>'
  44. print '<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">'
  45. print ' <fo:layout-master-set>'
  46. print ' <fo:simple-page-master master-name="page" page-height="20cm" page-width="15cm" margin="1cm">'
  47. print ' <fo:region-body/>'
  48. print ' </fo:simple-page-master>'
  49. print ' </fo:layout-master-set>'
  50. print ' <fo:page-sequence master-reference="page" font-size="14pt">'
  51. print ' <fo:flow flow-name="xsl-region-body">'
  52. print
  53. def printFOEnd():
  54. print ' </fo:flow>'
  55. print ' </fo:page-sequence>'
  56. print '</fo:root>'
  57. def printBorder(side, border, indent):
  58. """Prints out the border specifications.
  59. Params:
  60. side: one of 'before', 'after', 'start', 'end'
  61. border: a border specification
  62. indent: for pretty-printing, string of whitespaces to put before the border
  63. """
  64. print indent + ' border-' + side + '-width.length="' + border['length'] + '"'
  65. print indent + ' border-' + side + '-width.conditionality="' + border['cond'] + '"'
  66. print indent + ' border-' + side + '-style="solid"'
  67. print indent + ' border-' + side + '-color="' + border['color'] + '"'
  68. class TableGenerator:
  69. """Generates on stdout tables with no header and footer, with the border
  70. specifications passed to this object; and on stderr the corresponding resolved borders
  71. in a Java array, for inclusion into the test case. As soon as a pair of border sets is
  72. recorded, a table is generated.
  73. """
  74. fobjs = [
  75. ('<fo:table width="10cm" space-before="12pt" table-layout="fixed"', '>'),
  76. ('<fo:table-column column-width="proportional-column-width(1)"', '/>'),
  77. ('<fo:table-body', '>'),
  78. ('<fo:table-row', '>'),
  79. ('<fo:table-cell', '>')
  80. ]
  81. bordersBefore = None
  82. resBefore = {}
  83. """The comma between each table; nothing before the first one."""
  84. separator = ''
  85. def addBorderSet(self, borderSet, resolution):
  86. """Records a new border set, and prints out a table if its number is even.
  87. The first set will be used for borders-before, the second one for borders-after.
  88. Params:
  89. borderSet: a list of 5 border specifications for resp. table, table-column,
  90. table-body, table-row and table-cell
  91. resolution: the resolved border for the rest case (for the normal and leading
  92. cases the resolution is always the same)
  93. """
  94. if not self.bordersBefore:
  95. self.bordersBefore = borderSet
  96. self.resBefore = resolution
  97. else:
  98. # First the table
  99. for i in range(5):
  100. fobj = self.fobjs[i]
  101. indent = ' ' * (6 + 2*i)
  102. print indent + fobj[0]
  103. printBorder('before', self.bordersBefore[i], indent)
  104. printBorder('after', borderSet[i], indent)
  105. print indent + fobj[1]
  106. print ' <fo:block>Cell</fo:block>'
  107. print ' </fo:table-cell>'
  108. print ' </fo:table-row>'
  109. print ' </fo:table-body>'
  110. print ' </fo:table>'
  111. print
  112. # Then the resolution
  113. sys.stderr.write(self.separator + '{')
  114. comma = ''
  115. for beforeAfter in [self.resBefore, resolution]:
  116. sys.stderr.write(comma + '{border' + beforeAfter['length']
  117. + ', Color.' + beforeAfter['color'] + '}')
  118. comma = ', '
  119. sys.stderr.write('}')
  120. self.separator = ',\n'
  121. # Reset
  122. self.bordersBefore = None
  123. self.resBefore = {}
  124. class TableHFGenerator:
  125. """Generates on stdout tables with headers and footers, and the border specifications
  126. passed to this object; and on stderr the corresponding resolved borders in a Java
  127. array."""
  128. fobjs = [
  129. ['<fo:table width="10cm" space-before="12pt" table-layout="fixed"', '>'],
  130. ['<fo:table-column column-width="proportional-column-width(1)"', '/>'],
  131. ['<fo:table-header', '>'],
  132. ['<fo:table-row', '>'],
  133. ['<fo:table-cell', '>']
  134. ]
  135. borderHeader = [] # border-before for the header.
  136. borderFooter = [] # border-after for the footer.
  137. bordersBody = [] # borders for the cells in the body.
  138. def addBorderHeader(self, borders, resolution):
  139. self.borderHeader.append((borders, resolution))
  140. def addBorderFooter(self, borders, resolution):
  141. self.borderFooter.append((borders, resolution))
  142. def addBordersBody(self, borders, resolution):
  143. self.bordersBody.append((borders, resolution))
  144. def finish(self):
  145. """Prints out the tables and the resolved borders."""
  146. separator = '' # The comma between each table, none before the first one
  147. for tableNum in range(len(self.borderHeader)):
  148. # First the table
  149. print ' <!-- Table ' + str(tableNum) + ' -->'
  150. for i in range(2):
  151. fobj = self.fobjs[i]
  152. indent = ' ' * (6 + 2*i)
  153. print indent + fobj[0]
  154. printBorder('before', self.borderHeader[tableNum][0][i], indent)
  155. printBorder('after', self.borderFooter[tableNum][0][i], indent)
  156. print indent + fobj[1]
  157. self.fobjs[fo_body][0] = '<fo:table-header'
  158. for i in range(2, 5):
  159. fobj = self.fobjs[i]
  160. indent = ' ' * (6 + 2*i)
  161. print indent + fobj[0]
  162. printBorder('before', self.borderHeader[tableNum][0][i], indent)
  163. printBorder('after', self.bordersBody[tableNum][0][i-2], indent)
  164. print indent + fobj[1]
  165. print ' <fo:block>Header</fo:block>'
  166. print ' </fo:table-cell>'
  167. print ' </fo:table-row>'
  168. print ' </fo:table-header>'
  169. self.fobjs[fo_body][0] = '<fo:table-footer'
  170. for i in range(2, 5):
  171. fobj = self.fobjs[i]
  172. indent = ' ' * (6 + 2*i)
  173. print indent + fobj[0]
  174. printBorder('before', self.bordersBody[tableNum][0][i+7], indent)
  175. printBorder('after', self.borderFooter[tableNum][0][i], indent)
  176. print indent + fobj[1]
  177. print ' <fo:block>Footer</fo:block>'
  178. print ' </fo:table-cell>'
  179. print ' </fo:table-row>'
  180. print ' </fo:table-footer>'
  181. self.fobjs[fo_body][0] = '<fo:table-body'
  182. for i in range(2, 5):
  183. fobj = self.fobjs[i]
  184. indent = ' ' * (6 + 2*i)
  185. print indent + fobj[0]
  186. printBorder('before', {'length': '4pt', 'cond': 'discard', 'color': 'black'}, indent)
  187. printBorder('after', self.bordersBody[tableNum][0][i+1], indent)
  188. print indent + fobj[1]
  189. print ' <fo:block>Cell1</fo:block>'
  190. print ' </fo:table-cell>'
  191. print ' </fo:table-row>'
  192. print ' </fo:table-body>'
  193. for i in range(2, 5):
  194. fobj = self.fobjs[i]
  195. indent = ' ' * (6 + 2*i)
  196. print indent + fobj[0]
  197. printBorder('before', self.bordersBody[tableNum][0][i+4], indent)
  198. printBorder('after', {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, indent)
  199. print indent + fobj[1]
  200. print ' <fo:block>Cell1</fo:block>'
  201. print ' </fo:table-cell>'
  202. print ' </fo:table-row>'
  203. print ' </fo:table-body>'
  204. print ' </fo:table>'
  205. print
  206. # Then the resolutions
  207. sys.stderr.write(separator + '{')
  208. comma = ''
  209. for resHeadFoot in [self.borderHeader[tableNum][1], self.borderFooter[tableNum][1]]:
  210. for firstRest in ['first', 'rest']:
  211. sys.stderr.write(comma + '{border'
  212. + resHeadFoot[firstRest]['length']
  213. + ', Color.' + resHeadFoot[firstRest]['color']
  214. + '}')
  215. comma = ', '
  216. resBody = self.bordersBody[tableNum][1]
  217. for i in range(4):
  218. for normLeadRest in ['normal', 'lead', 'rest']:
  219. sys.stderr.write(', {border'
  220. + resBody[i][normLeadRest][0]
  221. + ', Color.' + resBody[i][normLeadRest][1]
  222. + '}')
  223. sys.stderr.write('}')
  224. separator = ',\n'
  225. def generateTestCases():
  226. """Generates testcases for table without header and footer."""
  227. def createAllCombinations():
  228. def createCombinations(n):
  229. if n == 0:
  230. allCombinations[0].append([])
  231. else:
  232. createCombinations(n-1)
  233. i = n
  234. while i > 0:
  235. for combinations in allCombinations[i-1]:
  236. allCombinations[i].append(copy.copy(combinations) + [n-1])
  237. i = i - 1
  238. allCombinations = [[] for i in range(6)]
  239. createCombinations(5)
  240. return allCombinations
  241. printFOStart()
  242. tableGenerator = TableGenerator()
  243. defaultBorders = []
  244. for color in ['black', 'red', 'magenta', 'blue', 'yellow']:
  245. defaultBorders.append({'length': '4pt', 'cond': 'discard', 'color': color})
  246. defaultBorders[fo_table]['length'] = '8pt'
  247. resolution = {'length': '0pt', 'color': 'black'}
  248. tableGenerator.addBorderSet(defaultBorders, resolution)
  249. for combinations in createAllCombinations()[1:]:
  250. for combination in combinations:
  251. retainedBorders = copy.deepcopy(defaultBorders)
  252. for index in combination:
  253. retainedBorders[index]['cond'] = 'retain'
  254. for index in combination:
  255. finalBorders = copy.deepcopy(retainedBorders)
  256. if index != fo_table:
  257. finalBorders[index]['length'] = '6pt'
  258. if fo_table in combination:
  259. resolution = {'length': '8pt', 'color': 'black'}
  260. else:
  261. resolution = {'length': '6pt', 'color': finalBorders[index]['color']}
  262. tableGenerator.addBorderSet(finalBorders, resolution)
  263. printFOEnd()
  264. def generateTestCasesHeaderFooter():
  265. """Generates testcases for table with headers and footers."""
  266. def generateBordersHeaderFooter(tableGenerator):
  267. defaultBorders = [
  268. {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # table
  269. {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # table-column
  270. {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # table-body
  271. {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # table-row
  272. {'length': '4pt', 'cond': 'discard', 'color': 'blue'} # table-cell
  273. ]
  274. defaultResolution = {
  275. 'first': {'length': '4pt', 'color': 'blue'},
  276. 'rest': {'length': '4pt', 'color': 'blue'}
  277. }
  278. for (winner, other) in [(fo_table, fo_column), (fo_column, fo_table)]:
  279. borders = copy.deepcopy(defaultBorders)
  280. borders[winner]['length'] = '8pt'
  281. resolution = copy.deepcopy(defaultResolution)
  282. resolution['first'] = {'length': '8pt', 'color': 'black'}
  283. for border in [
  284. ((other, '6pt', 'retain'), 'black'),
  285. ((fo_body, '6pt', 'discard'), 'blue'),
  286. ((fo_row, '6pt', 'discard'), 'blue'),
  287. ((fo_cell, '6pt', 'discard'), 'blue')
  288. ]:
  289. finalBorders = copy.deepcopy(borders)
  290. finalBorders[border[0][0]]['length'] = border[0][1]
  291. finalBorders[border[0][0]]['cond'] = border[0][2]
  292. finalResolution = copy.deepcopy(resolution)
  293. finalResolution['rest']['length'] = '6pt'
  294. finalResolution['rest']['color'] = border[1]
  295. tableGenerator.addBorderHeader(finalBorders, finalResolution)
  296. tableGenerator.addBorderFooter(finalBorders, finalResolution)
  297. def generateBordersBody(tableGenerator):
  298. # Named indices for readability
  299. header = 0
  300. rowh = 1
  301. cellh = 2
  302. body1 = 3
  303. row1 = 4
  304. cell1 = 5
  305. body2 = 6
  306. row2 = 7
  307. cell2 = 8
  308. footer = 9
  309. rowf = 10
  310. cellf = 11
  311. defaultBorders = [
  312. {'length': '4pt', 'cond': 'discard', 'color': 'red'}, # header
  313. {'length': '4pt', 'cond': 'discard', 'color': 'red'}, # header > row
  314. {'length': '4pt', 'cond': 'discard', 'color': 'red'}, # header > row > cell
  315. {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # body1
  316. {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # body1 > row
  317. {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # body1 > row > cell
  318. {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # body2
  319. {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # body2 > row
  320. {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # body2 > row > cell
  321. {'length': '4pt', 'cond': 'discard', 'color': 'magenta'}, # footer
  322. {'length': '4pt', 'cond': 'discard', 'color': 'magenta'}, # footer > row
  323. {'length': '4pt', 'cond': 'discard', 'color': 'magenta'} # footer > row > cell
  324. ]
  325. defaultResolution = [
  326. {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')}, # border-before cell 1
  327. {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'magenta')}, # border-after cell 1
  328. {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'red')}, # border-before cell 2
  329. {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')} # border-after cell 2
  330. ]
  331. # The following contains changes to the default borders. Depending on the object
  332. # targeted (in header, footer, body1 or body2), the affected border is either before
  333. # or after (the other one keeping its default value):
  334. # - for header: border-after
  335. # - for body1: border-after
  336. # - for body2: border-before
  337. # - for footer: border-before
  338. for setting in [
  339. {'borders': [(body2, '8pt', 'discard'), (body1, '6pt', 'discard')], 'res': [
  340. {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')},
  341. {'normal': ('8pt', 'blue'), 'lead': ('6pt', 'black'), 'rest': ('4pt', 'magenta')},
  342. {'normal': ('8pt', 'blue'), 'lead': ('8pt', 'blue'), 'rest': ('4pt', 'red')},
  343. {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]},
  344. {'borders': [(row2, '8pt', 'discard'), (row1, '6pt', 'retain')], 'res': [
  345. {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')},
  346. {'normal': ('8pt', 'blue'), 'lead': ('6pt', 'black'), 'rest': ('6pt', 'black')},
  347. {'normal': ('8pt', 'blue'), 'lead': ('8pt', 'blue'), 'rest': ('4pt', 'red')},
  348. {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]},
  349. {'borders': [(cell2, '6pt', 'retain'), (cellh, '8pt', 'discard'), (cell1, '4pt', 'retain')], 'res': [
  350. {'normal': ('8pt', 'red'), 'lead': ('8pt', 'red'), 'rest': ('8pt', 'red')},
  351. {'normal': ('6pt', 'blue'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'black')},
  352. {'normal': ('6pt', 'blue'), 'lead': ('8pt', 'red'), 'rest': ('8pt', 'red')},
  353. {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]},
  354. {'borders': [(body2, '6pt', 'retain'), (rowh, '8pt', 'discard'), (row1, '4pt', 'retain')], 'res': [
  355. {'normal': ('8pt', 'red'), 'lead': ('8pt', 'red'), 'rest': ('8pt', 'red')},
  356. {'normal': ('6pt', 'blue'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'magenta')},
  357. {'normal': ('6pt', 'blue'), 'lead': ('8pt', 'red'), 'rest': ('8pt', 'red')},
  358. {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]},
  359. # Almost a copy-paste of the above, swapping 1 and 2, header and footer
  360. {'borders': [(body1, '8pt', 'discard'), (body2, '6pt', 'discard')], 'res': [
  361. {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')},
  362. {'normal': ('8pt', 'black'), 'lead': ('8pt', 'black'), 'rest': ('4pt', 'magenta')},
  363. {'normal': ('8pt', 'black'), 'lead': ('6pt', 'blue'), 'rest': ('4pt', 'red')},
  364. {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]},
  365. {'borders': [(cell1, '8pt', 'discard'), (cell2, '6pt', 'retain')], 'res': [
  366. {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')},
  367. {'normal': ('8pt', 'black'), 'lead': ('8pt', 'black'), 'rest': ('4pt', 'magenta')},
  368. {'normal': ('8pt', 'black'), 'lead': ('6pt', 'blue'), 'rest': ('6pt', 'blue')},
  369. {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]},
  370. {'borders': [(row1, '6pt', 'retain'), (footer, '8pt', 'discard'), (body2, '4pt', 'retain')], 'res': [
  371. {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')},
  372. {'normal': ('6pt', 'black'), 'lead': ('8pt', 'magenta'), 'rest': ('8pt', 'magenta')},
  373. {'normal': ('6pt', 'black'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'red')},
  374. {'normal': ('8pt', 'magenta'), 'lead': ('8pt', 'magenta'), 'rest': ('8pt', 'magenta')}]},
  375. {'borders': [(body1, '8pt', 'retain'), (cellf, '6pt', 'discard'), (row2, '4pt', 'retain')], 'res': [
  376. {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')},
  377. {'normal': ('8pt', 'black'), 'lead': ('8pt', 'black'), 'rest': ('8pt', 'black')},
  378. {'normal': ('8pt', 'black'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'red')},
  379. {'normal': ('6pt', 'magenta'), 'lead': ('6pt', 'magenta'), 'rest': ('6pt', 'magenta')}]}]:
  380. finalBorders = copy.deepcopy(defaultBorders)
  381. for border in setting['borders']:
  382. finalBorders[border[0]]['length'] = border[1]
  383. finalBorders[border[0]]['cond'] = border[2]
  384. tableGenerator.addBordersBody(finalBorders, setting['res'])
  385. tableGenerator = TableHFGenerator()
  386. printFOStart()
  387. generateBordersHeaderFooter(tableGenerator)
  388. generateBordersBody(tableGenerator)
  389. tableGenerator.finish()
  390. printFOEnd()
  391. # Uncomment the appropriate line
  392. #generateTestCases()
  393. #generateTestCasesHeaderFooter()