From a6ced5bc251149ffa668fa77269e19423e36eff0 Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Wed, 30 Sep 2009 22:25:53 +0000 Subject: [PATCH] Minor improvements to error handling in ForkedEvaluator. Added junits. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@820479 13f79535-47bb-0310-9956-ffa450edef68 --- .../eval/forked/ForkedEvaluationSheet.java | 6 + .../poi/ss/formula/AllSSFormulaTests.java | 7 +- .../eval/forked/TestForkedEvaluator.java | 118 ++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/testcases/org/apache/poi/ss/formula/eval/forked/TestForkedEvaluator.java diff --git a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationSheet.java b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationSheet.java index c36232a1d..196bfa69d 100644 --- a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationSheet.java +++ b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationSheet.java @@ -27,6 +27,7 @@ import org.apache.poi.ss.formula.EvaluationWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellReference; /** * Represents a sheet being used for forked evaluation. Initially, objects of this class contain @@ -65,6 +66,11 @@ final class ForkedEvaluationSheet implements EvaluationSheet { ForkedEvaluationCell result = _sharedCellsByRowCol.get(key); if (result == null) { EvaluationCell mcell = _masterSheet.getCell(rowIndex, columnIndex); + if (mcell == null) { + CellReference cr = new CellReference(rowIndex, columnIndex); + throw new UnsupportedOperationException("Underlying cell '" + + cr.formatAsString() + "' is missing in master sheet."); + } result = new ForkedEvaluationCell(this, mcell); _sharedCellsByRowCol.put(key, result); } diff --git a/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java b/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java index 13e2cfecd..970d0f373 100644 --- a/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java +++ b/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java @@ -17,19 +17,22 @@ package org.apache.poi.ss.formula; +import org.apache.poi.ss.formula.eval.forked.TestForkedEvaluator; + import junit.framework.Test; import junit.framework.TestSuite; /** * Test suite for org.apache.poi.ss.formula - * + * * @author Josh Micich */ public final class AllSSFormulaTests { - public static Test suite() { + public static Test suite() { TestSuite result = new TestSuite(AllSSFormulaTests.class.getName()); result.addTestSuite(TestCellCacheEntry.class); result.addTestSuite(TestEvaluationCache.class); result.addTestSuite(TestWorkbookEvaluator.class); + result.addTestSuite(TestForkedEvaluator.class); return result; } } diff --git a/src/testcases/org/apache/poi/ss/formula/eval/forked/TestForkedEvaluator.java b/src/testcases/org/apache/poi/ss/formula/eval/forked/TestForkedEvaluator.java new file mode 100644 index 000000000..1e4806982 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/eval/forked/TestForkedEvaluator.java @@ -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. +==================================================================== */ + +package org.apache.poi.ss.formula.eval.forked; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.apache.poi.hssf.record.formula.eval.NumberEval; +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.formula.IStabilityClassifier; + +/** + * @author Josh Micich + */ +public final class TestForkedEvaluator extends TestCase { + /** + * set up a calculation workbook with input cells nicely segregated on a + * sheet called "Inputs" + */ + private static HSSFWorkbook createWorkbook() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet1 = wb.createSheet("Inputs"); + HSSFSheet sheet2 = wb.createSheet("Calculations"); + HSSFRow row; + row = sheet2.createRow(0); + row.createCell(0).setCellFormula("B1*Inputs!A1-Inputs!B1"); + row.createCell(1).setCellValue(5.0); // Calculations!B1 + + // some default input values + row = sheet1.createRow(0); + row.createCell(0).setCellValue(2.0); // Inputs!A1 + row.createCell(1).setCellValue(3.0); // Inputs!B1 + return wb; + } + + /** + * Shows a basic use-case for {@link ForkedEvaluator} + */ + public void testBasic() { + HSSFWorkbook wb = createWorkbook(); + + // The stability classifier is useful to reduce memory consumption of caching logic + IStabilityClassifier stabilityClassifier = new IStabilityClassifier() { + public boolean isCellFinal(int sheetIndex, int rowIndex, int columnIndex) { + return sheetIndex == 1; + } + }; + + ForkedEvaluator fe1 = ForkedEvaluator.create(wb, stabilityClassifier, null); + ForkedEvaluator fe2 = ForkedEvaluator.create(wb, stabilityClassifier, null); + + // fe1 and fe2 can be used concurrently on separate threads + + fe1.updateCell("Inputs", 0, 0, new NumberEval(4.0)); + fe1.updateCell("Inputs", 0, 1, new NumberEval(1.1)); + + fe2.updateCell("Inputs", 0, 0, new NumberEval(1.2)); + fe2.updateCell("Inputs", 0, 1, new NumberEval(2.0)); + + assertEquals(18.9, ((NumberEval) fe1.evaluate("Calculations", 0, 0)).getNumberValue(), 0.0); + assertEquals(4.0, ((NumberEval) fe2.evaluate("Calculations", 0, 0)).getNumberValue(), 0.0); + fe1.updateCell("Inputs", 0, 0, new NumberEval(3.0)); + assertEquals(13.9, ((NumberEval) fe1.evaluate("Calculations", 0, 0)).getNumberValue(), 0.0); + } + + /** + * As of Sep 2009, the Forked evaluator can update values from existing cells (this is because + * the underlying 'master' cell is used as a key into the calculation cache. Prior to the fix + * for this bug, an attempt to update a missing cell would result in NPE. This junit tests for + * a more meaningful error message.
+ * + * An alternate solution might involve allowing empty cells to be created as necessary. That + * was considered less desirable because so far, the underlying 'master' workbook is strictly + * read-only with respect to the ForkedEvaluator. + */ + public void testMissingInputCell() { + HSSFWorkbook wb = createWorkbook(); + + ForkedEvaluator fe = ForkedEvaluator.create(wb, null, null); + + // attempt update input at cell A2 (which is missing) + try { + fe.updateCell("Inputs", 1, 0, new NumberEval(4.0)); + throw new AssertionFailedError( + "Expected exception to be thrown due to missing input cell"); + } catch (NullPointerException e) { + StackTraceElement[] stes = e.getStackTrace(); + if (stes[0].getMethodName().equals("getIdentityKey")) { + throw new AssertionFailedError("Identified bug with update of missing input cell"); + } + throw e; + } catch (UnsupportedOperationException e) { + if (e.getMessage().equals( + "Underlying cell 'A2' is missing in master sheet.")) { + // expected during successful test + } else { + throw e; + } + } + } +}