Bug 46898: Convert unit tests for circular references to also run for XSSF

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1649122 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Dominik Stadler 2015-01-02 21:04:50 +00:00
parent 906a917796
commit 943af49680
9 changed files with 276 additions and 177 deletions

View File

@ -0,0 +1,31 @@
/* ====================================================================
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.
==================================================================== */
package org.apache.poi.ss.formula.eval;
import org.apache.poi.xssf.XSSFITestDataProvider;
/**
* Tests XSSFFormulaEvaluator for its handling of cell formula circular references.
*
* @author Josh Micich
*/
public final class TestXSSFCircularReferences extends BaseTestCircularReferences {
public TestXSSFCircularReferences() {
super(XSSFITestDataProvider.instance);
}
}

View File

@ -19,19 +19,21 @@
package org.apache.poi.xssf;
import org.apache.poi.POIDataSamples;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import org.apache.poi.POIDataSamples;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
/**
* @author Yegor Kozlov
*/
@ -43,6 +45,7 @@ public final class SXSSFITestDataProvider implements ITestDataProvider {
private SXSSFITestDataProvider() {
// enforce singleton
}
public Workbook openSampleWorkbook(String sampleFileName) {
XSSFWorkbook xssfWorkbook = XSSFITestDataProvider.instance.openSampleWorkbook(sampleFileName);
SXSSFWorkbook swb = new SXSSFWorkbook(xssfWorkbook);
@ -66,17 +69,25 @@ public final class SXSSFITestDataProvider implements ITestDataProvider {
}
return result;
}
public SXSSFWorkbook createWorkbook(){
SXSSFWorkbook wb = new SXSSFWorkbook();
instances.add(wb);
return wb;
}
public FormulaEvaluator createFormulaEvaluator(Workbook wb) {
return new XSSFFormulaEvaluator(((SXSSFWorkbook) wb).getXSSFWorkbook());
}
public byte[] getTestDataFileContent(String fileName) {
return POIDataSamples.getSpreadSheetInstance().readFile(fileName);
}
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL2007;
}
public String getStandardFileNameExtension() {
return "xlsx";
}

View File

@ -20,7 +20,9 @@ package org.apache.poi.xssf;
import org.apache.poi.POIDataSamples;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
/**
@ -32,24 +34,34 @@ public final class XSSFITestDataProvider implements ITestDataProvider {
private XSSFITestDataProvider() {
// enforce singleton
}
public XSSFWorkbook openSampleWorkbook(String sampleFileName) {
return XSSFTestDataSamples.openSampleWorkbook(sampleFileName);
}
public XSSFWorkbook writeOutAndReadBack(Workbook original) {
if(!(original instanceof XSSFWorkbook)) {
throw new IllegalArgumentException("Expected an instance of XSSFWorkbook");
}
return XSSFTestDataSamples.writeOutAndReadBack((XSSFWorkbook)original);
}
public XSSFWorkbook createWorkbook(){
return new XSSFWorkbook();
}
public FormulaEvaluator createFormulaEvaluator(Workbook wb) {
return new XSSFFormulaEvaluator((XSSFWorkbook) wb);
}
public byte[] getTestDataFileContent(String fileName) {
return POIDataSamples.getSpreadSheetInstance().readFile(fileName);
}
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL2007;
}
public String getStandardFileNameExtension() {
return "xlsx";
}

View File

@ -18,9 +18,11 @@
package org.apache.poi.hssf;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Workbook;
/**
@ -32,24 +34,34 @@ public final class HSSFITestDataProvider implements ITestDataProvider {
private HSSFITestDataProvider(){
// enforce singleton
}
public HSSFWorkbook openSampleWorkbook(String sampleFileName) {
return HSSFTestDataSamples.openSampleWorkbook(sampleFileName);
}
public HSSFWorkbook writeOutAndReadBack(Workbook original) {
if(!(original instanceof HSSFWorkbook)) {
throw new IllegalArgumentException("Expected an instance of HSSFWorkbook");
}
return HSSFTestDataSamples.writeOutAndReadBack((HSSFWorkbook)original);
}
public HSSFWorkbook createWorkbook(){
return new HSSFWorkbook();
}
public FormulaEvaluator createFormulaEvaluator(Workbook wb) {
return new HSSFFormulaEvaluator((HSSFWorkbook) wb);
}
public byte[] getTestDataFileContent(String fileName) {
return POIDataSamples.getSpreadSheetInstance().readFile(fileName);
}
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL97;
}
public String getStandardFileNameExtension() {
return "xls";
}

View File

@ -17,6 +17,7 @@
package org.apache.poi.ss;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Workbook;
/**
@ -45,6 +46,15 @@ public interface ITestDataProvider {
*/
Workbook createWorkbook();
/**
* Creates the corresponding {@link FormulaEvaluator} for the
* type of Workbook handled by this Provider.
*
* @param wb The workbook to base the formula evaluator on.
* @return A new instance of a matching type of formula evaluator.
*/
FormulaEvaluator createFormulaEvaluator(Workbook wb);
/**
* Opens a sample file from the standard HSSF test data directory
*

View File

@ -30,7 +30,7 @@ public class AllFormulaEvalTests {
public static Test suite() {
TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName());
result.addTestSuite(TestAreaEval.class);
result.addTestSuite(TestCircularReferences.class);
result.addTestSuite(TestHSSFCircularReferences.class);
result.addTestSuite(TestDivideEval.class);
result.addTestSuite(TestEqualEval.class);
result.addTestSuite(TestExternalFunction.class);

View File

@ -0,0 +1,161 @@
package org.apache.poi.ss.formula.eval;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Common superclass for testing cases of circular references
* both for HSSF and XSSF
*/
public abstract class BaseTestCircularReferences extends TestCase {
protected final ITestDataProvider _testDataProvider;
/**
* @param testDataProvider an object that provides test data in HSSF / XSSF specific way
*/
protected BaseTestCircularReferences(ITestDataProvider testDataProvider) {
_testDataProvider = testDataProvider;
}
/**
* Translates StackOverflowError into AssertionFailedError
*/
private CellValue evaluateWithCycles(Workbook wb, Cell testCell)
throws AssertionFailedError {
FormulaEvaluator evaluator = _testDataProvider.createFormulaEvaluator(wb);
try {
return evaluator.evaluate(testCell);
} catch (StackOverflowError e) {
throw new AssertionFailedError( "circular reference caused stack overflow error");
}
}
/**
* Makes sure that the specified evaluated cell value represents a circular reference error.
*/
private static void confirmCycleErrorCode(CellValue cellValue) {
assertTrue(cellValue.getCellType() == Cell.CELL_TYPE_ERROR);
assertEquals(ErrorEval.CIRCULAR_REF_ERROR.getErrorCode(), cellValue.getErrorValue());
}
/**
* ASF Bugzilla Bug 44413
* "INDEX() formula cannot contain its own location in the data array range"
*/
public void testIndexFormula() {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
int colB = 1;
sheet.createRow(0).createCell(colB).setCellValue(1);
sheet.createRow(1).createCell(colB).setCellValue(2);
sheet.createRow(2).createCell(colB).setCellValue(3);
Row row4 = sheet.createRow(3);
Cell testCell = row4.createCell(0);
// This formula should evaluate to the contents of B2,
testCell.setCellFormula("INDEX(A1:B4,2,2)");
// However the range A1:B4 also includes the current cell A4. If the other parameters
// were 4 and 1, this would represent a circular reference. Prior to v3.2 POI would
// 'fully' evaluate ref arguments before invoking operators, which raised the possibility of
// cycles / StackOverflowErrors.
CellValue cellValue = evaluateWithCycles(wb, testCell);
assertTrue(cellValue.getCellType() == Cell.CELL_TYPE_NUMERIC);
assertEquals(2, cellValue.getNumberValue(), 0);
}
/**
* Cell A1 has formula "=A1"
*/
public void testSimpleCircularReference() {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
Row row = sheet.createRow(0);
Cell testCell = row.createCell(0);
testCell.setCellFormula("A1");
CellValue cellValue = evaluateWithCycles(wb, testCell);
confirmCycleErrorCode(cellValue);
}
/**
* A1=B1, B1=C1, C1=D1, D1=A1
*/
public void testMultiLevelCircularReference() {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
Row row = sheet.createRow(0);
row.createCell(0).setCellFormula("B1");
row.createCell(1).setCellFormula("C1");
row.createCell(2).setCellFormula("D1");
Cell testCell = row.createCell(3);
testCell.setCellFormula("A1");
CellValue cellValue = evaluateWithCycles(wb, testCell);
confirmCycleErrorCode(cellValue);
}
public void testIntermediateCircularReferenceResults_bug46898() {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
Row row = sheet.createRow(0);
Cell cellA1 = row.createCell(0);
Cell cellB1 = row.createCell(1);
Cell cellC1 = row.createCell(2);
Cell cellD1 = row.createCell(3);
Cell cellE1 = row.createCell(4);
cellA1.setCellFormula("IF(FALSE, 1+B1, 42)");
cellB1.setCellFormula("1+C1");
cellC1.setCellFormula("1+D1");
cellD1.setCellFormula("1+E1");
cellE1.setCellFormula("1+A1");
FormulaEvaluator fe = _testDataProvider.createFormulaEvaluator(wb);
CellValue cv;
// Happy day flow - evaluate A1 first
cv = fe.evaluate(cellA1);
assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(42.0, cv.getNumberValue(), 0.0);
cv = fe.evaluate(cellB1); // no circ-ref-error because A1 result is cached
assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(46.0, cv.getNumberValue(), 0.0);
// Show the bug - evaluate another cell from the loop first
fe.clearAllCachedResultValues();
cv = fe.evaluate(cellB1);
if (cv.getCellType() == ErrorEval.CIRCULAR_REF_ERROR.getErrorCode()) {
throw new AssertionFailedError("Identified bug 46898");
}
assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(46.0, cv.getNumberValue(), 0.0);
// start evaluation on another cell
fe.clearAllCachedResultValues();
cv = fe.evaluate(cellE1);
assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(43.0, cv.getNumberValue(), 0.0);
}
}

View File

@ -1,169 +0,0 @@
/* ====================================================================
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.
==================================================================== */
package org.apache.poi.ss.formula.eval;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
/**
* Tests HSSFFormulaEvaluator for its handling of cell formula circular references.
*
* @author Josh Micich
*/
public final class TestCircularReferences extends TestCase {
/**
* Translates StackOverflowError into AssertionFailedError
*/
private static CellValue evaluateWithCycles(HSSFWorkbook wb, HSSFCell testCell)
throws AssertionFailedError {
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
try {
return evaluator.evaluate(testCell);
} catch (StackOverflowError e) {
throw new AssertionFailedError( "circular reference caused stack overflow error");
}
}
/**
* Makes sure that the specified evaluated cell value represents a circular reference error.
*/
private static void confirmCycleErrorCode(CellValue cellValue) {
assertTrue(cellValue.getCellType() == HSSFCell.CELL_TYPE_ERROR);
assertEquals(ErrorEval.CIRCULAR_REF_ERROR.getErrorCode(), cellValue.getErrorValue());
}
/**
* ASF Bugzilla Bug 44413
* "INDEX() formula cannot contain its own location in the data array range"
*/
public void testIndexFormula() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
int colB = 1;
sheet.createRow(0).createCell(colB).setCellValue(1);
sheet.createRow(1).createCell(colB).setCellValue(2);
sheet.createRow(2).createCell(colB).setCellValue(3);
HSSFRow row4 = sheet.createRow(3);
HSSFCell testCell = row4.createCell(0);
// This formula should evaluate to the contents of B2,
testCell.setCellFormula("INDEX(A1:B4,2,2)");
// However the range A1:B4 also includes the current cell A4. If the other parameters
// were 4 and 1, this would represent a circular reference. Prior to v3.2 POI would
// 'fully' evaluate ref arguments before invoking operators, which raised the possibility of
// cycles / StackOverflowErrors.
CellValue cellValue = evaluateWithCycles(wb, testCell);
assertTrue(cellValue.getCellType() == HSSFCell.CELL_TYPE_NUMERIC);
assertEquals(2, cellValue.getNumberValue(), 0);
}
/**
* Cell A1 has formula "=A1"
*/
public void testSimpleCircularReference() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFRow row = sheet.createRow(0);
HSSFCell testCell = row.createCell(0);
testCell.setCellFormula("A1");
CellValue cellValue = evaluateWithCycles(wb, testCell);
confirmCycleErrorCode(cellValue);
}
/**
* A1=B1, B1=C1, C1=D1, D1=A1
*/
public void testMultiLevelCircularReference() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFRow row = sheet.createRow(0);
row.createCell(0).setCellFormula("B1");
row.createCell(1).setCellFormula("C1");
row.createCell(2).setCellFormula("D1");
HSSFCell testCell = row.createCell(3);
testCell.setCellFormula("A1");
CellValue cellValue = evaluateWithCycles(wb, testCell);
confirmCycleErrorCode(cellValue);
}
public void testIntermediateCircularReferenceResults_bug46898() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFRow row = sheet.createRow(0);
HSSFCell cellA1 = row.createCell(0);
HSSFCell cellB1 = row.createCell(1);
HSSFCell cellC1 = row.createCell(2);
HSSFCell cellD1 = row.createCell(3);
HSSFCell cellE1 = row.createCell(4);
cellA1.setCellFormula("IF(FALSE, 1+B1, 42)");
cellB1.setCellFormula("1+C1");
cellC1.setCellFormula("1+D1");
cellD1.setCellFormula("1+E1");
cellE1.setCellFormula("1+A1");
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
CellValue cv;
// Happy day flow - evaluate A1 first
cv = fe.evaluate(cellA1);
assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(42.0, cv.getNumberValue(), 0.0);
cv = fe.evaluate(cellB1); // no circ-ref-error because A1 result is cached
assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(46.0, cv.getNumberValue(), 0.0);
// Show the bug - evaluate another cell from the loop first
fe.clearAllCachedResultValues();
cv = fe.evaluate(cellB1);
if (cv.getCellType() == ErrorEval.CIRCULAR_REF_ERROR.getErrorCode()) {
throw new AssertionFailedError("Identified bug 46898");
}
assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(46.0, cv.getNumberValue(), 0.0);
// start evaluation on another cell
fe.clearAllCachedResultValues();
cv = fe.evaluate(cellE1);
assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(43.0, cv.getNumberValue(), 0.0);
}
}

View File

@ -0,0 +1,31 @@
/* ====================================================================
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.
==================================================================== */
package org.apache.poi.ss.formula.eval;
import org.apache.poi.hssf.HSSFITestDataProvider;
/**
* Tests HSSFFormulaEvaluator for its handling of cell formula circular references.
*
* @author Josh Micich
*/
public final class TestHSSFCircularReferences extends BaseTestCircularReferences {
public TestHSSFCircularReferences() {
super(HSSFITestDataProvider.instance);
}
}