Fixed small bug in SUMIF() added junits. Also added test cases for DAYS360, some initially disabled.

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@883037 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2009-11-22 05:30:53 +00:00
parent e391cdb834
commit bfd968deb3
5 changed files with 270 additions and 8 deletions

View File

@ -63,7 +63,7 @@ public final class Sumif implements Function {
} catch (EvaluationException e) {
return e.getErrorEval();
}
I_MatchPredicate mp = Countif.createCriteriaPredicate(args[1], srcRowIndex, srcRowIndex);
I_MatchPredicate mp = Countif.createCriteriaPredicate(args[1], srcRowIndex, srcColumnIndex);
double result = sumMatchingCells(aeRange, mp, aeSum);
return new NumberEval(result);
}

View File

@ -22,7 +22,7 @@ import junit.framework.TestSuite;
/**
* Direct tests for all implementors of <code>Function</code>.
*
*
* @author Josh Micich
*/
public final class AllIndividualFunctionEvaluationTests {
@ -32,6 +32,7 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestAverage.class);
result.addTestSuite(TestCountFuncs.class);
result.addTestSuite(TestDate.class);
result.addTestSuite(TestDays360.class);
result.addTestSuite(TestFind.class);
result.addTestSuite(TestFinanceLib.class);
result.addTestSuite(TestIndex.class);
@ -47,6 +48,7 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestPmt.class);
result.addTestSuite(TestOffset.class);
result.addTestSuite(TestRowCol.class);
result.addTestSuite(TestSumif.class);
result.addTestSuite(TestSumproduct.class);
result.addTestSuite(TestStatsLib.class);
result.addTestSuite(TestTFunc.class);

View File

@ -29,7 +29,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* Test helper class for creating mock <code>Eval</code> objects
*
*
* @author Josh Micich
*/
public final class EvalFactory {
@ -39,7 +39,7 @@ public final class EvalFactory {
}
/**
* Creates a dummy AreaEval
* Creates a dummy AreaEval
* @param values empty (<code>null</code>) entries in this array will be converted to NumberEval.ZERO
*/
public static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
@ -48,7 +48,7 @@ public final class EvalFactory {
}
/**
* Creates a dummy AreaEval
* Creates a dummy AreaEval
* @param values empty (<code>null</code>) entries in this array will be converted to NumberEval.ZERO
*/
public static AreaEval createAreaEval(AreaPtg areaPtg, ValueEval[] values) {
@ -75,7 +75,7 @@ public final class EvalFactory {
public static RefEval createRefEval(String refStr, ValueEval value) {
return new MockRefEval(new RefPtg(refStr), value);
}
private static final class MockAreaEval extends AreaEvalBase {
private final ValueEval[] _values;
public MockAreaEval(AreaPtg areaPtg, ValueEval[] values) {
@ -94,10 +94,14 @@ public final class EvalFactory {
return _values[oneDimensionalIndex];
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
if (relFirstRowIx == 0 && relFirstColIx == 0
&& relLastRowIx == getHeight()-1 && relLastColIx == getWidth()-1) {
return this;
}
throw new RuntimeException("Operation not implemented on this mock object");
}
}
private static final class MockRefEval extends RefEvalBase {
private final ValueEval _value;
public MockRefEval(RefPtg ptg, ValueEval value) {
@ -115,5 +119,4 @@ public final class EvalFactory {
throw new RuntimeException("Operation not implemented on this mock object");
}
}
}

View File

@ -0,0 +1,155 @@
/* ====================================================================
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.hssf.record.formula.functions;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
/**
* @author Josh Micich
*/
public final class TestDays360 extends TestCase {
/**
* @param month 1-based
*/
private static Date makeDate(int year, int month, int day) {
Calendar cal = new GregorianCalendar(year, month-1, day, 0, 0, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
private static Date decrementDay(Date d) {
Calendar c = new GregorianCalendar();
c.setTimeInMillis(d.getTime());
c.add(Calendar.DAY_OF_MONTH, -1);
return c.getTime();
}
private static String fmt(Date d) {
Calendar c = new GregorianCalendar();
c.setTimeInMillis(d.getTime());
StringBuilder sb = new StringBuilder();
sb.append(c.get(Calendar.YEAR));
sb.append("/");
sb.append(c.get(Calendar.MONTH)+1);
sb.append("/");
sb.append(c.get(Calendar.DAY_OF_MONTH));
return sb.toString();
}
public void testBasic() {
confirm(120, 2009, 1, 15, 2009, 5, 15);
confirm(158, 2009, 1, 26, 2009, 7, 4);
// same results in leap years
confirm(120, 2008, 1, 15, 2008, 5, 15);
confirm(158, 2008, 1, 26, 2008, 7, 4);
// longer time spans
confirm(562, 2008, 8, 11, 2010, 3, 3);
confirm(916, 2007, 2, 23, 2009, 9, 9);
}
private static void confirm(int expResult, int y1, int m1, int d1, int y2, int m2, int d2) {
confirm(expResult, makeDate(y1, m1, d1), makeDate(y2, m2, d2), false);
confirm(-expResult, makeDate(y2, m2, d2), makeDate(y1, m1, d1), false);
}
/**
* The <tt>method</tt> parameter only makes a difference when the second parameter
* is the last day of the month that does <em>not</em> have 30 days.
*/
public void DISABLED_testMonthBoundaries() {
// jan
confirmMonthBoundary(false, 1, 0, 0, 2, 3, 4);
confirmMonthBoundary(true, 1, 0, 0, 1, 3, 4);
// feb
confirmMonthBoundary(false, 2,-2, 1, 2, 3, 4);
confirmMonthBoundary(true, 2, 0, 1, 2, 3, 4);
// mar
confirmMonthBoundary(false, 3, 0, 0, 2, 3, 4);
confirmMonthBoundary(true, 3, 0, 0, 1, 3, 4);
// apr
confirmMonthBoundary(false, 4, 0, 1, 2, 3, 4);
confirmMonthBoundary(true, 4, 0, 1, 2, 3, 4);
// may
confirmMonthBoundary(false, 5, 0, 0, 2, 3, 4);
confirmMonthBoundary(true, 5, 0, 0, 1, 3, 4);
// jun
confirmMonthBoundary(false, 6, 0, 1, 2, 3, 4);
confirmMonthBoundary(true, 6, 0, 1, 2, 3, 4);
// etc...
}
/**
* @param monthNo 1-based
* @param diffs
*/
private static void confirmMonthBoundary(boolean method, int monthNo, int...diffs) {
Date firstDayOfNextMonth = makeDate(2001, monthNo+1, 1);
Date secondArg = decrementDay(firstDayOfNextMonth);
Date firstArg = secondArg;
for (int i = 0; i < diffs.length; i++) {
int expResult = diffs[i];
confirm(expResult, firstArg, secondArg, method);
firstArg = decrementDay(firstArg);
}
}
private static void confirm(int expResult, Date firstArg, Date secondArg, boolean method) {
ValueEval ve;
if (method) {
// TODO enable 3rd arg -
ve = invokeDays360(convert(firstArg), convert(secondArg), BoolEval.valueOf(method));
} else {
ve = invokeDays360(convert(firstArg), convert(secondArg));
}
if (ve instanceof NumberEval) {
NumberEval numberEval = (NumberEval) ve;
if (numberEval.getNumberValue() != expResult) {
throw new AssertionFailedError(fmt(firstArg) + " " + fmt(secondArg) + " " + method +
" wrong result got (" + numberEval.getNumberValue()
+ ") but expected (" + expResult + ")");
}
// System.err.println(fmt(firstArg) + " " + fmt(secondArg) + " " + method + " success got (" + expResult + ")");
return;
}
throw new AssertionFailedError("wrong return type (" + ve.getClass().getName() + ")");
}
private static ValueEval invokeDays360(ValueEval...args) {
return new Days360().evaluate(args, -1, -1);
}
private static NumberEval convert(Date d) {
return new NumberEval(HSSFDateUtil.getExcelDate(d));
}
}

View File

@ -0,0 +1,102 @@
/* ====================================================================
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.hssf.record.formula.functions;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* Test cases for SUMPRODUCT()
*
* @author Josh Micich
*/
public final class TestSumif extends TestCase {
private static final NumberEval _30 = new NumberEval(30);
private static final NumberEval _40 = new NumberEval(40);
private static final NumberEval _50 = new NumberEval(50);
private static final NumberEval _60 = new NumberEval(60);
private static ValueEval invokeSumif(int rowIx, int colIx, ValueEval...args) {
return new Sumif().evaluate(args, rowIx, colIx);
}
private static void confirmDouble(double expected, ValueEval actualEval) {
if(!(actualEval instanceof NumericValueEval)) {
throw new AssertionFailedError("Expected numeric result");
}
NumericValueEval nve = (NumericValueEval)actualEval;
assertEquals(expected, nve.getNumberValue(), 0);
}
public void testBasic() {
ValueEval[] arg0values = new ValueEval[] { _30, _30, _40, _40, _50, _50 };
ValueEval[] arg2values = new ValueEval[] { _30, _40, _50, _60, _60, _60 };
AreaEval arg0;
AreaEval arg2;
arg0 = EvalFactory.createAreaEval("A3:B5", arg0values);
arg2 = EvalFactory.createAreaEval("D1:E3", arg2values);
confirm(60.0, arg0, new NumberEval(30.0));
confirm(70.0, arg0, new NumberEval(30.0), arg2);
confirm(100.0, arg0, new StringEval(">45"));
}
private static void confirm(double expectedResult, ValueEval...args) {
confirmDouble(expectedResult, invokeSumif(-1, -1, args));
}
/**
* test for bug observed near svn r882931
*/
public void testCriteriaArgRange() {
ValueEval[] arg0values = new ValueEval[] { _50, _60, _50, _50, _50, _30, };
ValueEval[] arg1values = new ValueEval[] { _30, _40, _50, _60, };
AreaEval arg0;
AreaEval arg1;
ValueEval ve;
arg0 = EvalFactory.createAreaEval("A3:B5", arg0values);
arg1 = EvalFactory.createAreaEval("A2:D2", arg1values); // single row range
ve = invokeSumif(0, 2, arg0, arg1); // invoking from cell C1
if (ve instanceof NumberEval) {
NumberEval ne = (NumberEval) ve;
if (ne.getNumberValue() == 30.0) {
throw new AssertionFailedError("identified error in SUMIF - criteria arg not evaluated properly");
}
}
confirmDouble(200, ve);
arg0 = EvalFactory.createAreaEval("C1:D3", arg0values);
arg1 = EvalFactory.createAreaEval("B1:B4", arg1values); // single column range
ve = invokeSumif(3, 0, arg0, arg1); // invoking from cell A4
confirmDouble(60, ve);
}
}