| #!/usr/bin/python |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| # $Id$ |
| |
| """A testcase generator for conditional borders and the collapsing border model, in the FO |
| tree. |
| Computes all the possible combinations of borders (retained/discarded, with/without header |
| and footer, etc.) and generates the corresponding tables together with the expected |
| resolved borders. |
| |
| The two functions to call are generateTestCases and generateTestCasesHeaderFooter; each of |
| them returns a complete FO file on stdout and a table of resolved borders on stderr, to be |
| included in the Java test case. This is all a bit rough be enough to get the testcases |
| generated. |
| |
| Type definitions: |
| border specification: |
| {'length': <string, e.g. '4pt'>, |
| 'cond': <'retain' or 'discard'> |
| 'color': <string, e.g. 'black'> |
| } |
| """ |
| |
| import sys; |
| import copy; |
| |
| fo_table = 0 |
| fo_column = 1 |
| fo_body = 2 |
| fo_row = 3 |
| fo_cell = 4 |
| |
| def printFOStart(): |
| print '<?xml version="1.0" standalone="no"?>' |
| print '<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">' |
| print ' <fo:layout-master-set>' |
| print ' <fo:simple-page-master master-name="page" page-height="20cm" page-width="15cm" margin="1cm">' |
| print ' <fo:region-body/>' |
| print ' </fo:simple-page-master>' |
| print ' </fo:layout-master-set>' |
| print ' <fo:page-sequence master-reference="page" font-size="14pt">' |
| print ' <fo:flow flow-name="xsl-region-body">' |
| print |
| |
| def printFOEnd(): |
| print ' </fo:flow>' |
| print ' </fo:page-sequence>' |
| print '</fo:root>' |
| |
| def printBorder(side, border, indent): |
| """Prints out the border specifications. |
| Params: |
| side: one of 'before', 'after', 'start', 'end' |
| border: a border specification |
| indent: for pretty-printing, string of whitespaces to put before the border |
| """ |
| print indent + ' border-' + side + '-width.length="' + border['length'] + '"' |
| print indent + ' border-' + side + '-width.conditionality="' + border['cond'] + '"' |
| print indent + ' border-' + side + '-style="solid"' |
| print indent + ' border-' + side + '-color="' + border['color'] + '"' |
| |
| class TableGenerator: |
| """Generates on stdout tables with no header and footer, with the border |
| specifications passed to this object; and on stderr the corresponding resolved borders |
| in a Java array, for inclusion into the test case. As soon as a pair of border sets is |
| recorded, a table is generated. |
| """ |
| |
| fobjs = [ |
| ('<fo:table width="10cm" space-before="12pt" table-layout="fixed"', '>'), |
| ('<fo:table-column column-width="proportional-column-width(1)"', '/>'), |
| ('<fo:table-body', '>'), |
| ('<fo:table-row', '>'), |
| ('<fo:table-cell', '>') |
| ] |
| |
| bordersBefore = None |
| resBefore = {} |
| """The comma between each table; nothing before the first one.""" |
| separator = '' |
| |
| def addBorderSet(self, borderSet, resolution): |
| """Records a new border set, and prints out a table if its number is even. |
| The first set will be used for borders-before, the second one for borders-after. |
| |
| Params: |
| borderSet: a list of 5 border specifications for resp. table, table-column, |
| table-body, table-row and table-cell |
| resolution: the resolved border for the rest case (for the normal and leading |
| cases the resolution is always the same) |
| """ |
| if not self.bordersBefore: |
| self.bordersBefore = borderSet |
| self.resBefore = resolution |
| else: |
| # First the table |
| for i in range(5): |
| fobj = self.fobjs[i] |
| indent = ' ' * (6 + 2*i) |
| print indent + fobj[0] |
| printBorder('before', self.bordersBefore[i], indent) |
| printBorder('after', borderSet[i], indent) |
| print indent + fobj[1] |
| print ' <fo:block>Cell</fo:block>' |
| print ' </fo:table-cell>' |
| print ' </fo:table-row>' |
| print ' </fo:table-body>' |
| print ' </fo:table>' |
| print |
| # Then the resolution |
| sys.stderr.write(self.separator + '{') |
| comma = '' |
| for beforeAfter in [self.resBefore, resolution]: |
| sys.stderr.write(comma + '{border' + beforeAfter['length'] |
| + ', Color.' + beforeAfter['color'] + '}') |
| comma = ', ' |
| sys.stderr.write('}') |
| self.separator = ',\n' |
| # Reset |
| self.bordersBefore = None |
| self.resBefore = {} |
| |
| class TableHFGenerator: |
| """Generates on stdout tables with headers and footers, and the border specifications |
| passed to this object; and on stderr the corresponding resolved borders in a Java |
| array.""" |
| |
| fobjs = [ |
| ['<fo:table width="10cm" space-before="12pt" table-layout="fixed"', '>'], |
| ['<fo:table-column column-width="proportional-column-width(1)"', '/>'], |
| ['<fo:table-header', '>'], |
| ['<fo:table-row', '>'], |
| ['<fo:table-cell', '>'] |
| ] |
| |
| borderHeader = [] # border-before for the header. |
| borderFooter = [] # border-after for the footer. |
| bordersBody = [] # borders for the cells in the body. |
| |
| def addBorderHeader(self, borders, resolution): |
| self.borderHeader.append((borders, resolution)) |
| |
| def addBorderFooter(self, borders, resolution): |
| self.borderFooter.append((borders, resolution)) |
| |
| def addBordersBody(self, borders, resolution): |
| self.bordersBody.append((borders, resolution)) |
| |
| def finish(self): |
| """Prints out the tables and the resolved borders.""" |
| separator = '' # The comma between each table, none before the first one |
| for tableNum in range(len(self.borderHeader)): |
| # First the table |
| print ' <!-- Table ' + str(tableNum) + ' -->' |
| for i in range(2): |
| fobj = self.fobjs[i] |
| indent = ' ' * (6 + 2*i) |
| print indent + fobj[0] |
| printBorder('before', self.borderHeader[tableNum][0][i], indent) |
| printBorder('after', self.borderFooter[tableNum][0][i], indent) |
| print indent + fobj[1] |
| self.fobjs[fo_body][0] = '<fo:table-header' |
| for i in range(2, 5): |
| fobj = self.fobjs[i] |
| indent = ' ' * (6 + 2*i) |
| print indent + fobj[0] |
| printBorder('before', self.borderHeader[tableNum][0][i], indent) |
| printBorder('after', self.bordersBody[tableNum][0][i-2], indent) |
| print indent + fobj[1] |
| print ' <fo:block>Header</fo:block>' |
| print ' </fo:table-cell>' |
| print ' </fo:table-row>' |
| print ' </fo:table-header>' |
| self.fobjs[fo_body][0] = '<fo:table-footer' |
| for i in range(2, 5): |
| fobj = self.fobjs[i] |
| indent = ' ' * (6 + 2*i) |
| print indent + fobj[0] |
| printBorder('before', self.bordersBody[tableNum][0][i+7], indent) |
| printBorder('after', self.borderFooter[tableNum][0][i], indent) |
| print indent + fobj[1] |
| print ' <fo:block>Footer</fo:block>' |
| print ' </fo:table-cell>' |
| print ' </fo:table-row>' |
| print ' </fo:table-footer>' |
| self.fobjs[fo_body][0] = '<fo:table-body' |
| for i in range(2, 5): |
| fobj = self.fobjs[i] |
| indent = ' ' * (6 + 2*i) |
| print indent + fobj[0] |
| printBorder('before', {'length': '4pt', 'cond': 'discard', 'color': 'black'}, indent) |
| printBorder('after', self.bordersBody[tableNum][0][i+1], indent) |
| print indent + fobj[1] |
| print ' <fo:block>Cell1</fo:block>' |
| print ' </fo:table-cell>' |
| print ' </fo:table-row>' |
| print ' </fo:table-body>' |
| for i in range(2, 5): |
| fobj = self.fobjs[i] |
| indent = ' ' * (6 + 2*i) |
| print indent + fobj[0] |
| printBorder('before', self.bordersBody[tableNum][0][i+4], indent) |
| printBorder('after', {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, indent) |
| print indent + fobj[1] |
| print ' <fo:block>Cell1</fo:block>' |
| print ' </fo:table-cell>' |
| print ' </fo:table-row>' |
| print ' </fo:table-body>' |
| print ' </fo:table>' |
| print |
| # Then the resolutions |
| sys.stderr.write(separator + '{') |
| comma = '' |
| for resHeadFoot in [self.borderHeader[tableNum][1], self.borderFooter[tableNum][1]]: |
| for firstRest in ['first', 'rest']: |
| sys.stderr.write(comma + '{border' |
| + resHeadFoot[firstRest]['length'] |
| + ', Color.' + resHeadFoot[firstRest]['color'] |
| + '}') |
| comma = ', ' |
| resBody = self.bordersBody[tableNum][1] |
| for i in range(4): |
| for normLeadRest in ['normal', 'lead', 'rest']: |
| sys.stderr.write(', {border' |
| + resBody[i][normLeadRest][0] |
| + ', Color.' + resBody[i][normLeadRest][1] |
| + '}') |
| sys.stderr.write('}') |
| separator = ',\n' |
| |
| def generateTestCases(): |
| """Generates testcases for table without header and footer.""" |
| def createAllCombinations(): |
| def createCombinations(n): |
| if n == 0: |
| allCombinations[0].append([]) |
| else: |
| createCombinations(n-1) |
| i = n |
| while i > 0: |
| for combinations in allCombinations[i-1]: |
| allCombinations[i].append(copy.copy(combinations) + [n-1]) |
| i = i - 1 |
| allCombinations = [[] for i in range(6)] |
| createCombinations(5) |
| return allCombinations |
| |
| printFOStart() |
| tableGenerator = TableGenerator() |
| |
| defaultBorders = [] |
| for color in ['black', 'red', 'magenta', 'blue', 'yellow']: |
| defaultBorders.append({'length': '4pt', 'cond': 'discard', 'color': color}) |
| defaultBorders[fo_table]['length'] = '8pt' |
| |
| resolution = {'length': '0pt', 'color': 'black'} |
| tableGenerator.addBorderSet(defaultBorders, resolution) |
| for combinations in createAllCombinations()[1:]: |
| for combination in combinations: |
| retainedBorders = copy.deepcopy(defaultBorders) |
| for index in combination: |
| retainedBorders[index]['cond'] = 'retain' |
| for index in combination: |
| finalBorders = copy.deepcopy(retainedBorders) |
| if index != fo_table: |
| finalBorders[index]['length'] = '6pt' |
| if fo_table in combination: |
| resolution = {'length': '8pt', 'color': 'black'} |
| else: |
| resolution = {'length': '6pt', 'color': finalBorders[index]['color']} |
| tableGenerator.addBorderSet(finalBorders, resolution) |
| printFOEnd() |
| |
| def generateTestCasesHeaderFooter(): |
| """Generates testcases for table with headers and footers.""" |
| def generateBordersHeaderFooter(tableGenerator): |
| defaultBorders = [ |
| {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # table |
| {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # table-column |
| {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # table-body |
| {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # table-row |
| {'length': '4pt', 'cond': 'discard', 'color': 'blue'} # table-cell |
| ] |
| defaultResolution = { |
| 'first': {'length': '4pt', 'color': 'blue'}, |
| 'rest': {'length': '4pt', 'color': 'blue'} |
| } |
| for (winner, other) in [(fo_table, fo_column), (fo_column, fo_table)]: |
| borders = copy.deepcopy(defaultBorders) |
| borders[winner]['length'] = '8pt' |
| resolution = copy.deepcopy(defaultResolution) |
| resolution['first'] = {'length': '8pt', 'color': 'black'} |
| for border in [ |
| ((other, '6pt', 'retain'), 'black'), |
| ((fo_body, '6pt', 'discard'), 'blue'), |
| ((fo_row, '6pt', 'discard'), 'blue'), |
| ((fo_cell, '6pt', 'discard'), 'blue') |
| ]: |
| finalBorders = copy.deepcopy(borders) |
| finalBorders[border[0][0]]['length'] = border[0][1] |
| finalBorders[border[0][0]]['cond'] = border[0][2] |
| finalResolution = copy.deepcopy(resolution) |
| finalResolution['rest']['length'] = '6pt' |
| finalResolution['rest']['color'] = border[1] |
| tableGenerator.addBorderHeader(finalBorders, finalResolution) |
| tableGenerator.addBorderFooter(finalBorders, finalResolution) |
| |
| def generateBordersBody(tableGenerator): |
| # Named indices for readability |
| header = 0 |
| rowh = 1 |
| cellh = 2 |
| body1 = 3 |
| row1 = 4 |
| cell1 = 5 |
| body2 = 6 |
| row2 = 7 |
| cell2 = 8 |
| footer = 9 |
| rowf = 10 |
| cellf = 11 |
| |
| defaultBorders = [ |
| {'length': '4pt', 'cond': 'discard', 'color': 'red'}, # header |
| {'length': '4pt', 'cond': 'discard', 'color': 'red'}, # header > row |
| {'length': '4pt', 'cond': 'discard', 'color': 'red'}, # header > row > cell |
| {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # body1 |
| {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # body1 > row |
| {'length': '4pt', 'cond': 'discard', 'color': 'black'}, # body1 > row > cell |
| {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # body2 |
| {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # body2 > row |
| {'length': '4pt', 'cond': 'discard', 'color': 'blue'}, # body2 > row > cell |
| {'length': '4pt', 'cond': 'discard', 'color': 'magenta'}, # footer |
| {'length': '4pt', 'cond': 'discard', 'color': 'magenta'}, # footer > row |
| {'length': '4pt', 'cond': 'discard', 'color': 'magenta'} # footer > row > cell |
| ] |
| defaultResolution = [ |
| {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')}, # border-before cell 1 |
| {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'magenta')}, # border-after cell 1 |
| {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'red')}, # border-before cell 2 |
| {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')} # border-after cell 2 |
| ] |
| # The following contains changes to the default borders. Depending on the object |
| # targeted (in header, footer, body1 or body2), the affected border is either before |
| # or after (the other one keeping its default value): |
| # - for header: border-after |
| # - for body1: border-after |
| # - for body2: border-before |
| # - for footer: border-before |
| for setting in [ |
| {'borders': [(body2, '8pt', 'discard'), (body1, '6pt', 'discard')], 'res': [ |
| {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')}, |
| {'normal': ('8pt', 'blue'), 'lead': ('6pt', 'black'), 'rest': ('4pt', 'magenta')}, |
| {'normal': ('8pt', 'blue'), 'lead': ('8pt', 'blue'), 'rest': ('4pt', 'red')}, |
| {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]}, |
| {'borders': [(row2, '8pt', 'discard'), (row1, '6pt', 'retain')], 'res': [ |
| {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')}, |
| {'normal': ('8pt', 'blue'), 'lead': ('6pt', 'black'), 'rest': ('6pt', 'black')}, |
| {'normal': ('8pt', 'blue'), 'lead': ('8pt', 'blue'), 'rest': ('4pt', 'red')}, |
| {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]}, |
| {'borders': [(cell2, '6pt', 'retain'), (cellh, '8pt', 'discard'), (cell1, '4pt', 'retain')], 'res': [ |
| {'normal': ('8pt', 'red'), 'lead': ('8pt', 'red'), 'rest': ('8pt', 'red')}, |
| {'normal': ('6pt', 'blue'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'black')}, |
| {'normal': ('6pt', 'blue'), 'lead': ('8pt', 'red'), 'rest': ('8pt', 'red')}, |
| {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]}, |
| {'borders': [(body2, '6pt', 'retain'), (rowh, '8pt', 'discard'), (row1, '4pt', 'retain')], 'res': [ |
| {'normal': ('8pt', 'red'), 'lead': ('8pt', 'red'), 'rest': ('8pt', 'red')}, |
| {'normal': ('6pt', 'blue'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'magenta')}, |
| {'normal': ('6pt', 'blue'), 'lead': ('8pt', 'red'), 'rest': ('8pt', 'red')}, |
| {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]}, |
| # Almost a copy-paste of the above, swapping 1 and 2, header and footer |
| {'borders': [(body1, '8pt', 'discard'), (body2, '6pt', 'discard')], 'res': [ |
| {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')}, |
| {'normal': ('8pt', 'black'), 'lead': ('8pt', 'black'), 'rest': ('4pt', 'magenta')}, |
| {'normal': ('8pt', 'black'), 'lead': ('6pt', 'blue'), 'rest': ('4pt', 'red')}, |
| {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]}, |
| {'borders': [(cell1, '8pt', 'discard'), (cell2, '6pt', 'retain')], 'res': [ |
| {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')}, |
| {'normal': ('8pt', 'black'), 'lead': ('8pt', 'black'), 'rest': ('4pt', 'magenta')}, |
| {'normal': ('8pt', 'black'), 'lead': ('6pt', 'blue'), 'rest': ('6pt', 'blue')}, |
| {'normal': ('4pt', 'blue'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'magenta')}]}, |
| {'borders': [(row1, '6pt', 'retain'), (footer, '8pt', 'discard'), (body2, '4pt', 'retain')], 'res': [ |
| {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')}, |
| {'normal': ('6pt', 'black'), 'lead': ('8pt', 'magenta'), 'rest': ('8pt', 'magenta')}, |
| {'normal': ('6pt', 'black'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'red')}, |
| {'normal': ('8pt', 'magenta'), 'lead': ('8pt', 'magenta'), 'rest': ('8pt', 'magenta')}]}, |
| {'borders': [(body1, '8pt', 'retain'), (cellf, '6pt', 'discard'), (row2, '4pt', 'retain')], 'res': [ |
| {'normal': ('4pt', 'black'), 'lead': ('4pt', 'black'), 'rest': ('4pt', 'red')}, |
| {'normal': ('8pt', 'black'), 'lead': ('8pt', 'black'), 'rest': ('8pt', 'black')}, |
| {'normal': ('8pt', 'black'), 'lead': ('4pt', 'blue'), 'rest': ('4pt', 'red')}, |
| {'normal': ('6pt', 'magenta'), 'lead': ('6pt', 'magenta'), 'rest': ('6pt', 'magenta')}]}]: |
| finalBorders = copy.deepcopy(defaultBorders) |
| for border in setting['borders']: |
| finalBorders[border[0]]['length'] = border[1] |
| finalBorders[border[0]]['cond'] = border[2] |
| tableGenerator.addBordersBody(finalBorders, setting['res']) |
| |
| tableGenerator = TableHFGenerator() |
| printFOStart() |
| generateBordersHeaderFooter(tableGenerator) |
| generateBordersBody(tableGenerator) |
| tableGenerator.finish() |
| printFOEnd() |
| |
| # Uncomment the appropriate line |
| #generateTestCases() |
| #generateTestCasesHeaderFooter() |