Unit tests and a bugfix related to a repeatable sub-sequence specifier combo git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1296104 13f79535-47bb-0310-9956-ffa450edef68pull/26/head
@@ -218,7 +218,7 @@ public class PageSequenceMaster extends FObj { | |||
if (nextSubSequence == null) { | |||
//Sub-sequence exhausted so attempt to reuse it | |||
blockLevelEventProducer.pageSequenceMasterExhausted(this, | |||
masterName, canRecover, getLocator()); | |||
masterName, canRecover & currentSubSequence.isReusable(), getLocator()); | |||
currentSubSequence.reset(); | |||
if (!currentSubSequence.canProcess(mainFlowName)) { | |||
throw new PageProductionException( |
@@ -233,6 +233,9 @@ public class RepeatablePageMasterAlternatives extends FObj | |||
return getMaximumRepeats() == INFINITE; | |||
} | |||
/** {@inheritDoc} */ | |||
public boolean isReusable() { | |||
return false; | |||
} | |||
} |
@@ -181,6 +181,9 @@ public class RepeatablePageMasterReference extends FObj | |||
return getMaximumRepeats() == INFINITE; | |||
} | |||
/** {@inheritDoc} */ | |||
public boolean isReusable() { | |||
return false; | |||
} | |||
} |
@@ -159,5 +159,10 @@ public class SinglePageMasterReference extends FObj | |||
return false; | |||
} | |||
/** {@inheritDoc} */ | |||
public boolean isReusable() { | |||
return true; | |||
} | |||
} | |||
@@ -82,5 +82,12 @@ public interface SubSequenceSpecifier { | |||
*/ | |||
boolean isInfinite(); | |||
/** | |||
* Test if this can be reused when it is the last sub-sequence specifer, | |||
* and has been exhausted | |||
* @return true if and only if it can be reused | |||
*/ | |||
boolean isReusable(); | |||
} | |||
@@ -330,7 +330,6 @@ public class PageProvider implements Constants { | |||
boolean isFirstPage = (startPageOfPageSequence == index); | |||
SimplePageMaster spm = pageSeq.getNextSimplePageMaster( | |||
index, isFirstPage, isLastPage, isBlank); | |||
Page page = new Page(spm, index, pageNumberString, isBlank, spanAll); | |||
//Set unique key obtained from the AreaTreeHandler | |||
page.getPageViewport().setKey(areaTreeHandler.generatePageViewportKey()); |
@@ -148,12 +148,18 @@ public class PageSequenceLayoutManager extends AbstractPageSequenceLayoutManager | |||
@Override | |||
protected Page makeNewPage(boolean isBlank) { | |||
Page newPage; | |||
Page newPage = super.makeNewPage(isBlank); | |||
do { | |||
newPage = super.makeNewPage(isBlank); | |||
} while (!getPageSequence().getMainFlow().getFlowName() | |||
.equals(newPage.getSimplePageMaster().getRegion(FO_REGION_BODY).getRegionName())); | |||
// Empty pages (pages that have been generated from a SPM that has an un-mapped flow name) | |||
// cannot layout areas from the main flow. Blank pages can be created from empty pages. | |||
if (!isBlank) { | |||
while (!getPageSequence().getMainFlow().getFlowName() | |||
.equals(newPage.getSimplePageMaster() | |||
.getRegion(FO_REGION_BODY).getRegionName())) { | |||
newPage = super.makeNewPage(isBlank); | |||
} | |||
} | |||
return newPage; | |||
} |
@@ -36,6 +36,7 @@ import org.apache.fop.image.loader.batik.ImagePreloaderTestCase; | |||
import org.apache.fop.intermediate.IFMimickingTestCase; | |||
import org.apache.fop.render.extensions.prepress.PageBoundariesTestCase; | |||
import org.apache.fop.render.extensions.prepress.PageScaleTestCase; | |||
import org.apache.fop.layoutmgr.PageSequenceLayoutManagerTestCase; | |||
import org.apache.fop.render.pdf.PDFAConformanceTestCase; | |||
import org.apache.fop.render.pdf.PDFCMapTestCase; | |||
import org.apache.fop.render.pdf.PDFEncodingTestCase; | |||
@@ -62,6 +63,7 @@ import org.apache.fop.pdf.PDFLibraryTestSuite; | |||
ImageLoaderTestCase.class, | |||
ImagePreloaderTestCase.class, | |||
IFMimickingTestCase.class, | |||
PageSequenceLayoutManagerTestCase.class, | |||
PageBoundariesTestCase.class, | |||
PageScaleTestCase.class, | |||
org.apache.fop.afp.AFPTestSuite.class, |
@@ -21,14 +21,13 @@ package org.apache.fop.fo.flow.table; | |||
import org.junit.runner.RunWith; | |||
import org.junit.runners.Suite; | |||
import org.junit.runners.Suite.SuiteClasses; | |||
/** | |||
* All test to be added in FOTreeTestSuite | |||
* | |||
*/ | |||
@RunWith(Suite.class) | |||
@SuiteClasses({ | |||
@Suite.SuiteClasses({ | |||
CollapsedConditionalBorderTestCase.class, | |||
IllegalRowSpanTestCase.class, | |||
RowGroupBuilderTestCase.class, |
@@ -29,7 +29,5 @@ import org.junit.runner.RunWith; | |||
@RunWith(Suite.class) | |||
@Suite.SuiteClasses({ PageSequenceMasterTestCase.class, | |||
RepeatablePageMasterAlternativesTestCase.class}) | |||
public final class AllTests { | |||
} |
@@ -23,14 +23,18 @@ import static org.junit.Assert.fail; | |||
import static org.mockito.Matchers.anyBoolean; | |||
import static org.mockito.Matchers.anyInt; | |||
import static org.mockito.Matchers.anyObject; | |||
import static org.mockito.Matchers.anyString; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.fo.FONode; | |||
import org.apache.fop.layoutmgr.BlockLevelEventProducer; | |||
import org.junit.Test; | |||
import org.xml.sax.Locator; | |||
/** | |||
@@ -39,57 +43,119 @@ import org.junit.Test; | |||
*/ | |||
public class PageSequenceMasterTestCase { | |||
/** | |||
* Test that PageProductionException is thrown if the final simple-page-master | |||
* cannot handle the main-flow of the page sequence | |||
* @throws Exception exception | |||
*/ | |||
@Test | |||
public void testGetNextSimplePageMasterException() throws Exception { | |||
final String mainFlowRegionName = "main"; | |||
final String emptyFlowRegionName = "empty"; | |||
// Create stubs | |||
FONode mockParent = mock(FONode.class); | |||
Root mockRoot = mock(Root.class); | |||
LayoutMasterSet mockLayoutMasterSet = mock(LayoutMasterSet.class); | |||
// This will represent a page master that does not map to the main flow | |||
// of the page sequence | |||
SimplePageMaster mockEmptySPM = mock(SimplePageMaster.class); | |||
Region mockRegion = mock(Region.class); | |||
SinglePageMasterReference mockSinglePageMasterReference | |||
= mock(SinglePageMasterReference.class); | |||
BlockLevelEventProducer mockBlockLevelEventProducer = mock(BlockLevelEventProducer.class); | |||
//Stub behaviour | |||
when(mockParent.getRoot()).thenReturn(mockRoot); | |||
when(mockRoot.getLayoutMasterSet()).thenReturn(mockLayoutMasterSet); | |||
//The layout master set should return the empty page master | |||
when(mockLayoutMasterSet.getSimplePageMaster(anyString())).thenReturn(mockEmptySPM); | |||
when(mockEmptySPM.getRegion(anyInt())).thenReturn(mockRegion); | |||
when(mockRegion.getRegionName()).thenReturn(emptyFlowRegionName); | |||
when(mockSinglePageMasterReference.getNextPageMaster(anyBoolean(), anyBoolean(), | |||
anyBoolean(), anyBoolean())) | |||
.thenReturn(null, mockEmptySPM); | |||
PageSequenceMaster pageSequenceMaster = new PageSequenceMaster(mockParent, | |||
mockBlockLevelEventProducer); | |||
pageSequenceMaster.startOfNode(); | |||
pageSequenceMaster.addSubsequenceSpecifier(mockSinglePageMasterReference); | |||
try { | |||
pageSequenceMaster.getNextSimplePageMaster(false, false, false, false, | |||
mainFlowRegionName); | |||
fail("The next simple page master does not refer to the main flow"); | |||
} catch (PageProductionException ppe) { | |||
//Passed test | |||
} | |||
} | |||
/** | |||
* Test that block level events are produced in line with | |||
* XSL:FO - 6.4.8 fo:page-sequence-master - | |||
* "It is an error if the entire sequence of sub-sequence-specifiers children is exhausted | |||
* while some areas returned by an fo:flow are not placed. Implementations may recover, | |||
* if possible, by re-using the sub-sequence-specifier that was last used to generate a page." | |||
* | |||
* @throws Exception exception | |||
*/ | |||
@Test | |||
public void testGetNextSimplePageMasterExhausted() throws Exception { | |||
//Test when the last sub-sequence specifier is not repeatable | |||
testGetNextSimplePageMasterExhausted(true); | |||
//Test when the last sub-sequence specifier is repeatable | |||
testGetNextSimplePageMasterExhausted(false); | |||
} | |||
private void testGetNextSimplePageMasterExhausted(boolean canResume) throws Exception { | |||
SimplePageMaster spm = mock(SimplePageMaster.class); | |||
SubSequenceSpecifier mockSinglePageMasterReference | |||
= mock(SubSequenceSpecifier.class); | |||
BlockLevelEventProducer mockBlockLevelEventProducer = mock(BlockLevelEventProducer.class); | |||
// subject under test | |||
PageSequenceMaster pageSequenceMaster = createPageSequenceMaster( | |||
mockBlockLevelEventProducer); | |||
pageSequenceMaster.addSubsequenceSpecifier(mockSinglePageMasterReference); | |||
//Setup to mock the exhaustion of the last sub-sequence specifier | |||
when(mockSinglePageMasterReference.getNextPageMaster(anyBoolean(), anyBoolean(), | |||
anyBoolean(), anyBoolean())).thenReturn(null, spm); | |||
//Need this for the method to return normally | |||
when(mockSinglePageMasterReference.canProcess(anyString())).thenReturn(true); | |||
when(mockSinglePageMasterReference.isReusable()).thenReturn(canResume); | |||
pageSequenceMaster.getNextSimplePageMaster(false, false, false, false, null); | |||
verify(mockBlockLevelEventProducer).pageSequenceMasterExhausted((Locator)anyObject(), | |||
anyString(), eq(canResume), (Locator)anyObject()); | |||
} | |||
/** | |||
* Test that PageProductionException is thrown if the final simple-page-master | |||
* cannot handle the main-flow of the page sequence | |||
* @throws Exception exception | |||
*/ | |||
@Test | |||
public void testGetNextSimplePageMasterException() throws Exception { | |||
final String mainFlowRegionName = "main"; | |||
final String emptyFlowRegionName = "empty"; | |||
// This will represent a page master that does not map to the main flow | |||
// of the page sequence | |||
SimplePageMaster mockEmptySPM = mock(SimplePageMaster.class); | |||
Region mockRegion = mock(Region.class); | |||
SinglePageMasterReference mockSinglePageMasterReference | |||
= mock(SinglePageMasterReference.class); | |||
BlockLevelEventProducer mockBlockLevelEventProducer = mock(BlockLevelEventProducer.class); | |||
LayoutMasterSet mockLayoutMasterSet = mock(LayoutMasterSet.class); | |||
//The layout master set should return the empty page master | |||
when(mockLayoutMasterSet.getSimplePageMaster(anyString())).thenReturn(mockEmptySPM); | |||
when(mockEmptySPM.getRegion(anyInt())).thenReturn(mockRegion); | |||
when(mockRegion.getRegionName()).thenReturn(emptyFlowRegionName); | |||
when(mockSinglePageMasterReference.getNextPageMaster(anyBoolean(), anyBoolean(), | |||
anyBoolean(), anyBoolean())) | |||
.thenReturn(null, mockEmptySPM); | |||
PageSequenceMaster pageSequenceMaster = createPageSequenceMaster(mockLayoutMasterSet, | |||
mockBlockLevelEventProducer); | |||
pageSequenceMaster.startOfNode(); | |||
pageSequenceMaster.addSubsequenceSpecifier(mockSinglePageMasterReference); | |||
try { | |||
pageSequenceMaster.getNextSimplePageMaster(false, false, false, false, | |||
mainFlowRegionName); | |||
fail("The next simple page master does not refer to the main flow"); | |||
} catch (PageProductionException ppe) { | |||
//Passed test | |||
} | |||
} | |||
private PageSequenceMaster createPageSequenceMaster( | |||
BlockLevelEventProducer blockLevelEventProducer) throws FOPException { | |||
return createPageSequenceMaster(mock(LayoutMasterSet.class), blockLevelEventProducer); | |||
} | |||
private PageSequenceMaster createPageSequenceMaster(LayoutMasterSet layoutMasterSet, | |||
BlockLevelEventProducer blockLevelEventProducer) throws FOPException { | |||
FONode mockParent = mock(FONode.class); | |||
Root mockRoot = mock(Root.class); | |||
//Stub generic components | |||
when(mockParent.getRoot()).thenReturn(mockRoot); | |||
when(mockRoot.getLayoutMasterSet()).thenReturn(layoutMasterSet); | |||
PageSequenceMaster psm = new PageSequenceMaster(mockParent, blockLevelEventProducer); | |||
psm.startOfNode(); | |||
return psm; | |||
} | |||
} | |||
@@ -25,21 +25,18 @@ import static org.mockito.Matchers.anyInt; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import org.junit.Test; | |||
import org.apache.fop.fo.Constants; | |||
import org.apache.fop.fo.PropertyList; | |||
import org.apache.fop.fo.expr.NumericProperty; | |||
import org.apache.fop.fo.properties.Property; | |||
import org.junit.Test; | |||
/** | |||
* Unit Test for RepeatablePageMasterAlternatives | |||
* | |||
*/ | |||
public class RepeatablePageMasterAlternativesTestCase | |||
implements Constants { | |||
public class RepeatablePageMasterAlternativesTestCase implements Constants { | |||
/** | |||
* |
@@ -34,4 +34,5 @@ import org.junit.runners.Suite; | |||
org.apache.fop.fo.DelegatingFOEventHandlerTestCase.class | |||
}) | |||
public final class FOTreeTestSuite { | |||
} |
@@ -0,0 +1,118 @@ | |||
/* | |||
* 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:$ */ | |||
package org.apache.fop.layoutmgr; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.mockito.Matchers.anyInt; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import org.apache.fop.area.AreaTreeHandler; | |||
import org.apache.fop.area.PageViewport; | |||
import org.apache.fop.fo.pagination.Flow; | |||
import org.apache.fop.fo.pagination.PageSequence; | |||
import org.apache.fop.fo.pagination.Region; | |||
import org.apache.fop.fo.pagination.Root; | |||
import org.apache.fop.fo.pagination.SimplePageMaster; | |||
import org.junit.Test; | |||
public class PageSequenceLayoutManagerTestCase { | |||
private static final String MAIN_FLOW_NAME = "main"; | |||
private static final String EMPTY_FLOW_NAME = "empty"; | |||
/** | |||
* Blank pages can be created from empty pages | |||
* | |||
* @throws Exception | |||
*/ | |||
@Test | |||
public void testGetNextPageBlank() throws Exception { | |||
final Page expectedPage = createPageForRegionName(EMPTY_FLOW_NAME); | |||
final Page[] providedPages = new Page[]{expectedPage}; | |||
testGetNextPage(providedPages, expectedPage, true); | |||
} | |||
/** | |||
* Empty pages should not be provided by the PageSequenceLayoutManager | |||
* to layout the main flow | |||
* | |||
* @throws Exception | |||
*/ | |||
@Test | |||
public void testGetNextPageFirstEmpty() throws Exception { | |||
final Page emptyPage = createPageForRegionName(EMPTY_FLOW_NAME); | |||
final Page expectedPage = createPageForRegionName(MAIN_FLOW_NAME); | |||
final Page[] providedPages = new Page[]{emptyPage, expectedPage}; | |||
testGetNextPage(providedPages, expectedPage, false); | |||
} | |||
private void testGetNextPage(final Page[] providedPages, Page expectedPage, boolean isBlank) { | |||
final Flow flow = mock(Flow.class); | |||
final PageSequence pseq = mock(PageSequence.class); | |||
final Root root = mock(Root.class); | |||
final AreaTreeHandler ath = mock(AreaTreeHandler.class); | |||
when(flow.getFlowName()).thenReturn(MAIN_FLOW_NAME); | |||
when(pseq.getMainFlow()).thenReturn(flow); | |||
when(pseq.getRoot()).thenReturn(root); | |||
PageSequenceLayoutManager sut = new PageSequenceLayoutManager(ath, pseq) { | |||
@Override | |||
protected Page createPage(int i, boolean b) { | |||
return providedPages[i - 1]; | |||
} | |||
@Override | |||
protected void finishPage() { | |||
//nop | |||
} | |||
// Expose the protected method for testing | |||
public Page makeNewPage(boolean isBlank) { | |||
return super.makeNewPage(isBlank); | |||
} | |||
}; | |||
assertEquals(expectedPage, sut.makeNewPage(isBlank)); | |||
} | |||
private static Page createPageForRegionName(final String regionName) { | |||
final Page page = mock(Page.class); | |||
final SimplePageMaster spm = mock(SimplePageMaster.class); | |||
final PageViewport pageViewport = mock(PageViewport.class); | |||
final Region region = mock(Region.class); | |||
when(page.getSimplePageMaster()).thenReturn(spm); | |||
when(page.getPageViewport()).thenReturn(pageViewport); | |||
when(spm.getRegion(anyInt())).thenReturn(region); | |||
when(region.getRegionName()).thenReturn(regionName); | |||
return page; | |||
} | |||
} |
@@ -0,0 +1,65 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!-- | |||
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$ --> | |||
<testcase> | |||
<info> | |||
<p> | |||
This test checks that when the region-body@region-name of simple-page-master does not map to | |||
the main flow, the PageSequenceLayoutManager treats the generated Page as an empty/blank page. | |||
</p> | |||
</info> | |||
<fo> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="main" page-width="2in" page-height="2in"> | |||
<fo:region-body region-name="main" margin-top="1in"/> | |||
<fo:region-before region-name="main-before"/> | |||
</fo:simple-page-master> | |||
<fo:simple-page-master master-name="empty" page-width="2in" page-height="2in"> | |||
<fo:region-body region-name="empty"/> | |||
<fo:region-before region-name="empty-before" /> | |||
</fo:simple-page-master> | |||
<fo:page-sequence-master master-name="psm"> | |||
<fo:repeatable-page-master-reference maximum-repeats="1" master-reference="main"/> | |||
<fo:repeatable-page-master-reference maximum-repeats="1" master-reference="empty"/> | |||
<fo:repeatable-page-master-reference master-reference="main"/> | |||
</fo:page-sequence-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="psm"> | |||
<fo:static-content flow-name="main-before"> | |||
<fo:block background-color="#afa">main <fo:page-number/></fo:block> | |||
</fo:static-content> | |||
<fo:static-content flow-name="empty-before"> | |||
<fo:block background-color="#faa">empty <fo:page-number/></fo:block> | |||
</fo:static-content> | |||
<fo:flow flow-name="main"> | |||
<fo:block break-before="page">flow 1</fo:block> | |||
<fo:block break-before="page">flow 2</fo:block> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> | |||
</fo> | |||
<checks> | |||
<eval expected="main-before" xpath="//pageViewport[1]//regionBefore/@name"/> | |||
<eval expected="empty-before" xpath="//pageViewport[2]//regionBefore/@name"/> | |||
<eval expected="main-before" xpath="//pageViewport[3]//regionBefore/@name"/> | |||
</checks> | |||
</testcase> |