From b7ab04e5f288d1f5c1bf07d2815a71638e9071fe Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Sun, 4 Jan 2015 10:23:45 +0000 Subject: [PATCH] Update tests for ExcelAntWorkbookUtil, remove some unnecessary null-checks that can never happen, fix setting failed state on error in evaluation git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1649311 13f79535-47bb-0310-9956-ffa450edef68 --- .../util/ExcelAntEvaluationResult.java | 6 +- .../excelant/util/ExcelAntWorkbookUtil.java | 22 +- .../util/TestExcelAntWorkbookUtil.java | 281 +++++++++++++----- test-data/spreadsheet/excelant.xls | Bin 37888 -> 23552 bytes 4 files changed, 216 insertions(+), 93 deletions(-) diff --git a/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntEvaluationResult.java b/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntEvaluationResult.java index 6633357157..ae946dd8cd 100644 --- a/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntEvaluationResult.java +++ b/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntEvaluationResult.java @@ -30,7 +30,7 @@ public class ExcelAntEvaluationResult { /** * This boolean flag is used to determine if the evaluation completed * without error. This alone doesn't ensure that the evaluation was - * sucessful. + * successful. */ private boolean evaluationCompletedWithError ; @@ -108,7 +108,5 @@ public class ExcelAntEvaluationResult { + ", returnValue=" + returnValue + ", errorMessage=" + errorMessage + ", actualDelta=" + actualDelta + ", cellName=" + cellName + "]"; - } - - + } } diff --git a/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntWorkbookUtil.java b/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntWorkbookUtil.java index eec6157a65..c4f6969edd 100644 --- a/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntWorkbookUtil.java +++ b/src/excelant/java/org/apache/poi/ss/excelant/util/ExcelAntWorkbookUtil.java @@ -52,7 +52,7 @@ public class ExcelAntWorkbookUtil extends Typedef { private Workbook workbook; - private HashMap xlsMacroList; + private final HashMap xlsMacroList = new HashMap(); /** * Constructs an instance using a String that contains the fully qualified @@ -63,7 +63,6 @@ public class ExcelAntWorkbookUtil extends Typedef { */ protected ExcelAntWorkbookUtil(String fName) { excelFileName = fName; - xlsMacroList = new HashMap() ; loadWorkbook(); } @@ -75,7 +74,6 @@ public class ExcelAntWorkbookUtil extends Typedef { */ protected ExcelAntWorkbookUtil(Workbook wb) { workbook = wb; - xlsMacroList = new HashMap() ; } /** @@ -164,14 +162,14 @@ public class ExcelAntWorkbookUtil extends Typedef { protected FormulaEvaluator getEvaluator( String fileName ) { FormulaEvaluator evaluator ; if (fileName.endsWith(".xlsx")) { - if( xlsMacroList != null && xlsMacroList.size() > 0 ) { + if( xlsMacroList.size() > 0 ) { evaluator = XSSFFormulaEvaluator.create( (XSSFWorkbook) workbook, null, getFunctions() ) ; } evaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook); } else { - if( xlsMacroList != null && xlsMacroList.size() > 0 ) { + if( xlsMacroList.size() > 0 ) { evaluator = HSSFFormulaEvaluator.create( (HSSFWorkbook)workbook, null, getFunctions() ) ; @@ -312,7 +310,7 @@ public class ExcelAntWorkbookUtil extends Typedef { Byte.toString( resultOfEval.getErrorValue() ) ; } - evalResults = new ExcelAntEvaluationResult(false, false, + evalResults = new ExcelAntEvaluationResult(true, false, resultOfEval.getNumberValue(), "Evaluation failed due to an evaluation error of " + resultOfEval.getErrorValue() @@ -331,10 +329,7 @@ public class ExcelAntWorkbookUtil extends Typedef { */ public String getCellAsString( String cellName ) { Cell cell = getCell( cellName ) ; - if( cell != null ) { - return cell.getStringCellValue() ; - } - return "" ; + return cell.getStringCellValue() ; } @@ -346,10 +341,7 @@ public class ExcelAntWorkbookUtil extends Typedef { */ public double getCellAsDouble( String cellName ) { Cell cell = getCell( cellName ) ; - if( cell != null ) { - return cell.getNumericCellValue() ; - } - return 0.0 ; + return cell.getNumericCellValue() ; } /** * Returns a cell reference based on a String in standard Excel format @@ -360,7 +352,6 @@ public class ExcelAntWorkbookUtil extends Typedef { * @return */ private Cell getCell(String cellName) { - CellReference cellRef = new CellReference(cellName); String sheetName = cellRef.getSheetName(); Sheet sheet = workbook.getSheet(sheetName); @@ -384,5 +375,4 @@ public class ExcelAntWorkbookUtil extends Typedef { return cell; } - } diff --git a/src/excelant/testcases/org/apache/poi/ss/excelant/util/TestExcelAntWorkbookUtil.java b/src/excelant/testcases/org/apache/poi/ss/excelant/util/TestExcelAntWorkbookUtil.java index 7e5b1e4534..afc330b9f8 100644 --- a/src/excelant/testcases/org/apache/poi/ss/excelant/util/TestExcelAntWorkbookUtil.java +++ b/src/excelant/testcases/org/apache/poi/ss/excelant/util/TestExcelAntWorkbookUtil.java @@ -48,15 +48,15 @@ public class TestExcelAntWorkbookUtil extends TestCase { } public void testStringConstructor() { - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - assertNotNull( fixture ) ; + assertNotNull(fixture); } public void testLoadNotExistingFile() { try { - assertNotNull(new ExcelAntWorkbookUtilTestHelper( + assertNotNull(new ExcelAntWorkbookUtilTestHelper( "notexistingFile" )); fail("Should catch exception here"); } catch (BuildException e) { @@ -69,152 +69,287 @@ public class TestExcelAntWorkbookUtil extends TestCase { FileInputStream fis = new FileInputStream(workbookFile); Workbook workbook = WorkbookFactory.create(fis); - fixture = new ExcelAntWorkbookUtilTestHelper( workbook ) ; - - assertNotNull( fixture ) ; + fixture = new ExcelAntWorkbookUtilTestHelper(workbook); + assertNotNull(fixture); } public void testAddFunction() { - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - assertNotNull( fixture ) ; + assertNotNull(fixture); - fixture.addFunction("h2_ZFactor", new CalculateMortgageFunction() ) ; + fixture.addFunction("h2_ZFactor", new CalculateMortgageFunction()); - UDFFinder functions = fixture.getFunctions() ; + UDFFinder functions = fixture.getFunctions(); - assertNotNull( functions ) ; + assertNotNull(functions); + assertNotNull(functions.findFunction("h2_ZFactor")); } + public void testAddFunctionClassName() throws Exception { + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + + assertNotNull(fixture); + + fixture.addFunction("h2_ZFactor", CalculateMortgageFunction.class.getName()); + + UDFFinder functions = fixture.getFunctions(); + + assertNotNull(functions); + assertNotNull(functions.findFunction("h2_ZFactor")); + } + + public void testAddFunctionInvalidClassName() throws Exception { + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + + assertNotNull(fixture); + + fixture.addFunction("h2_ZFactor", String.class.getName()); + + UDFFinder functions = fixture.getFunctions(); + + assertNotNull(functions); + assertNull(functions.findFunction("h2_ZFactor")); + } + public void testGetWorkbook() { - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - assertNotNull( fixture ) ; + assertNotNull(fixture); - Workbook workbook = fixture.getWorkbook() ; + Workbook workbook = fixture.getWorkbook(); - assertNotNull( workbook ) ; + assertNotNull(workbook); } public void testFileName() { - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - assertNotNull( fixture ) ; + assertNotNull(fixture); - String fileName = fixture.getFileName() ; + String fileName = fixture.getFileName(); - assertNotNull( fileName ) ; + assertNotNull(fileName); - assertEquals( mortgageCalculatorFileName, fileName ) ; + assertEquals(mortgageCalculatorFileName, fileName); } public void testGetEvaluator() { - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; - - FormulaEvaluator evaluator = fixture.getEvaluator( - mortgageCalculatorFileName ) ; - - assertNotNull( evaluator ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + FormulaEvaluator evaluator = fixture.getEvaluator( + mortgageCalculatorFileName); + assertNotNull(evaluator); } + public void testGetEvaluatorWithUDF() { + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + + fixture.addFunction("h2_ZFactor", new CalculateMortgageFunction()); + + FormulaEvaluator evaluator = fixture.getEvaluator( + mortgageCalculatorFileName); + + assertNotNull(evaluator); + } + public void testGetEvaluatorXLSX() { - fixture = new ExcelAntWorkbookUtilTestHelper( - BuildFileTest.getDataDir() + "/spreadsheet/sample.xlsx") ; + fixture = new ExcelAntWorkbookUtilTestHelper( + BuildFileTest.getDataDir() + "/spreadsheet/sample.xlsx"); - FormulaEvaluator evaluator = fixture.getEvaluator( - BuildFileTest.getDataDir() + "/spreadsheet/sample.xlsx" ) ; + FormulaEvaluator evaluator = fixture.getEvaluator( + BuildFileTest.getDataDir() + "/spreadsheet/sample.xlsx"); - assertNotNull( evaluator ) ; + assertNotNull(evaluator); } + public void testGetEvaluatorXLSXWithFunction() { + fixture = new ExcelAntWorkbookUtilTestHelper( + BuildFileTest.getDataDir() + "/spreadsheet/sample.xlsx"); + + fixture.addFunction("h2_ZFactor", new CalculateMortgageFunction()); + + FormulaEvaluator evaluator = fixture.getEvaluator( + BuildFileTest.getDataDir() + "/spreadsheet/sample.xlsx"); + + assertNotNull(evaluator); + } + public void testEvaluateCell() { String cell = "'MortgageCalculator'!B4" ; double expectedValue = 790.79 ; double precision = 0.1 ; - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - ExcelAntEvaluationResult result = fixture.evaluateCell( cell, + ExcelAntEvaluationResult result = fixture.evaluateCell(cell, expectedValue, - precision ) ; - - System.out.println( result ) ; - - assertTrue( result.didTestPass() ) ; + precision); + + //System.out.println(result); + assertTrue(result.toString().contains("evaluationCompletedWithError=false")); + assertTrue(result.toString().contains("returnValue=790.79")); + assertTrue(result.toString().contains("cellName='MortgageCalculator'!B4")); + + assertFalse(result.evaluationCompleteWithError()); + assertTrue(result.didTestPass()); } + public void testEvaluateCellFailedPrecision() { + String cell = "'MortgageCalculator'!B4" ; + double expectedValue = 790.79 ; + double precision = 0.0000000000001 ; + + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + + ExcelAntEvaluationResult result = fixture.evaluateCell(cell, + expectedValue, + precision); + + //System.out.println(result); + assertTrue(result.toString().contains("evaluationCompletedWithError=false")); + assertTrue(result.toString().contains("returnValue=790.79")); + assertTrue(result.toString().contains("cellName='MortgageCalculator'!B4")); + + assertFalse(result.evaluationCompleteWithError()); + assertFalse(result.didTestPass()); + } + + public void testEvaluateCellWithError() { + String cell = "'ErrorCell'!A1" ; + double expectedValue = 790.79 ; + double precision = 0.1 ; + + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + + ExcelAntEvaluationResult result = fixture.evaluateCell(cell, + expectedValue, + precision); + + System.out.println(result); + assertTrue(result.toString().contains("evaluationCompletedWithError=true")); + assertTrue(result.toString().contains("returnValue=0.0")); + assertTrue(result.toString().contains("cellName='ErrorCell'!A1")); + + assertTrue(result.evaluationCompleteWithError()); + assertFalse(result.didTestPass()); + } + public void testGetSheets() { - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - ArrayList sheets = fixture.getSheets() ; + ArrayList sheets = fixture.getSheets(); - assertNotNull( sheets ) ; - assertEquals( sheets.size(), 3 ) ; + assertNotNull(sheets); + assertEquals(sheets.size(), 3); } public void testSetString() { String cell = "'MortgageCalculator'!C14" ; String cellValue = "testString" ; - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - fixture.setStringValue( cell, cellValue ) ; + fixture.setStringValue(cell, cellValue); - String value = fixture.getCellAsString( cell ) ; - - assertNotNull( value ) ; - - assertEquals( cellValue, value ) ; + String value = fixture.getCellAsString(cell); + assertNotNull(value); + assertEquals(cellValue, value); } + public void testSetNotExistingSheet() { + String cell = "'NotexistingSheet'!C14" ; + + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + try { + fixture.setStringValue(cell, "some"); + fail("Should catch exception here"); + } catch (BuildException e) { + assertTrue(e.getMessage().contains("NotexistingSheet")); + } + } + + public void testSetFormula() { + String cell = "'MortgageCalculator'!C14" ; + String cellValue = "SUM(B14:B18)" ; + + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + + fixture.setFormulaValue(cell, cellValue); + + double value = fixture.getCellAsDouble(cell); + + assertNotNull(value); + assertEquals(0.0, value); + } + + public void testSetDoubleValue() { + String cell = "'MortgageCalculator'!C14" ; + double cellValue = 1.2; + + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); + + fixture.setDoubleValue(cell, cellValue); + + Double value = fixture.getCellAsDouble(cell); + + assertNotNull(value); + assertEquals(cellValue, value); + } + public void testSetDate() { String cell = "'MortgageCalculator'!C14" ; Date cellValue = new Date(); - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - fixture.setDateValue( cell, cellValue ) ; + fixture.setDateValue(cell, cellValue); - double value = fixture.getCellAsDouble( cell ) ; - - assertNotNull( value ) ; - - assertEquals( DateUtil.getExcelDate(cellValue, false), value ) ; + double value = fixture.getCellAsDouble(cell); + assertNotNull(value); + assertEquals(DateUtil.getExcelDate(cellValue, false), value); } public void testGetNonexistingString() { String cell = "'MortgageCalculator'!C33" ; - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - String value = fixture.getCellAsString( cell ) ; + String value = fixture.getCellAsString(cell); - assertEquals( "", value ) ; + assertEquals("", value); } public void testGetNonexistingDouble() { String cell = "'MortgageCalculator'!C33" ; - fixture = new ExcelAntWorkbookUtilTestHelper( - mortgageCalculatorFileName ) ; + fixture = new ExcelAntWorkbookUtilTestHelper( + mortgageCalculatorFileName); - double value = fixture.getCellAsDouble( cell ) ; + double value = fixture.getCellAsDouble(cell); - assertEquals( 0.0, value ) ; + assertEquals(0.0, value); } } diff --git a/test-data/spreadsheet/excelant.xls b/test-data/spreadsheet/excelant.xls index e41edfbfd219f7bf212be379e76b8dc483edf07a..24f1fb3605baca1efde8c28611e997a7a5ae5187 100644 GIT binary patch delta 3817 zcmb_feQ;FO6+idwdoR0TLzaBh2$C*l89uVb#1ISuvLOj3G=z^(8mH(aB%2tF2I7X% z9mjoU#OX{ahTL)V4`iQ9t2w8vUzW3He zWz48I`Mq=RIrpA>?(d#^_dV+o%{nZGOBO664^5_hWYQN!pgPZO28cA&JqK!!2_ubC zAbr|0qY%0u$|mhHZxFRf=%uAyGGjg(3xI_{IS`gR2Jhb6etbl4)_JhpYlL*wdu&Ow zpEYjzwic7U5y^=StK@QRi@a}RsHg%nv-)yJstKRpSadO0mO085kXHdKfrvb(UF2u$ z)s9sLTNm4bwzYy)+TPvv^EK;CCo&z?^E1@rw!^;|&(>;M5$u!b~w)?A*T z98rg0`luJ-4B%NwGwB5Sq!Yuub0~(WRTl2`U96?LpB^Tx;p&E#hK;q&rFuRk_E9dv z50!<>$|@FBRGgwB%=j>kr-MATQ<6c}b2v)>(bV+3@UQEit(R~51M+2m$u}YKb&fbt zK|wbfNH@@Od*YO9a`g)?pbuX_ztGhu98azzpKCZzp9#o&{=`K4>_Cwtc1Z@MhK5CH zmI2G7>2{SvyJ!w=Gvtw~vL(-{bS$8?{R6R{j-DuI-(X)y ztbbq^&7hswS0W!Z3=H%S)J6OHb|XV8sZus94ktGKQqbG9jq-_7TUA76iVr*aiSrn@ zhz;Iy25%Wl*0ZdlX(`U-Bjm;39l}TCO^OJf5h{Hj=Y{f6hcoX*z$Zk3vlQmkQ{H`Y zXz_xYS%#X9I=#gJOin3wq{a)d4__er@X70{=gNDkXBI3}VU?@r5T1hM{_6U<57DKl zc44r@w;$^@+@3L(g32)>db3`(xj)ASCl zM^`P?h;9M$rkEL?l$pyXPl|8DeA3l+3G$@^a*ke@r%|rz$-NBO(sJ>}+1#7w=HVWW zLiAuada{_tl(_M5-4Z>CEyRyT>-EuStnxb$@0$YgI}+a~#k_5tmc{C6;uhsaBczV3Od2MrTWj4>jY@R{T6nI9jES|wSiU9junB@458s$iai=%7a8cwdmH zL?$883kRL9(hBI1dM$~dRFI$ zdpigEZ|?7o1siVZjP?bWUh$(%9b0iPM%6F+s^0GI8o6W1N_pUlQkmKuG;uC)LCF3@ zv3zN`C{cpoaH**nw?lGX-2!m#-3jo<4+Ff|2LWE|?*P8^4g-Adjsv(V=_P>Ue+!rf z@DD0@QqFk}Kjvcpr_T|U+G_g;$Z0@j8`$}_n|DULqM42vn@#Ii^}PM{{WtdB)O$m) zHP+G97adTO@Pw5>1aM2lt>C3Z8LVB0Pj4Fi8(;ObZ`pIkXY-yHV68?bhjn$IW(GJF z_Ntxy$I1H_%?ikSy;L4P^M`uqj4$c7K*veQQ8PG79?KM5NDp(SG}W&dt=s)4F*2N# z$zND9nTQ-5ig*sTS>BOYk^MGT5^L8VzzBI;tR%=Ud%r>B5#UkaG2piV@7eDG`9v&I z!JUqv!SX2NUw~sy`)SB$ocamKzXCEe7}8(hO7mba!Q1>Y@CxuMa0(a$UIShS-T>YN z{sa6M;B+_*{112=cnA1D@GkHkkOJNZJ^(%hIBmv(j{*F~(x<=~U;_AzsF2R00{&=%36OMbOoKe#jLlNHM&_)g%_6p$PALOE9tn0#82buz9u<*DuE zQv+`KOJcrO?U(Vp*Z8>)>$vcDL?LHo6Tn5#I=lu)k#kE$t~}aRWmZ93gf9lYDOU|M z(9XfQdG5DFd=DmLXM7}Gr{-cb8ikzL6F06zH70L0))`*Za(S%!dO$;!kv(;`10R|h z2g}ju(`5XPxPLXO7Qki1YV`7%cKaEPX#7~-x_*uAgU`8Z6PxAu`W9Qy=RTwFmV+A{ zJ>TE1-rR}CzddrI(^--IPGQqz-o?fo8Mj*aizPEi{92A%j9~l;d4afBr6X^~P~DhUA%b&O(Z^Z056QjCo`{8aMxm>Wg`DY{{DM<%{sVo%kFc C4}h}( literal 37888 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