mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-25 13:48:53 +08:00
Test cases for functions for https://github.com/unidoc/unioffice/issues/336 (#363)
* SUBSTITUTE * ACCRINTM fix * tests * MID
This commit is contained in:
parent
fbcea8d7d6
commit
2cfb3539c7
@ -384,6 +384,9 @@ func Accrintm(args []Result) Result {
|
|||||||
if errResult.Type == ResultTypeError {
|
if errResult.Type == ResultTypeError {
|
||||||
return errResult
|
return errResult
|
||||||
}
|
}
|
||||||
|
if issueDate >= settlementDate {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "Issue date should be earlier than settlement date")
|
||||||
|
}
|
||||||
if args[2].Type != ResultTypeNumber {
|
if args[2].Type != ResultTypeNumber {
|
||||||
return MakeErrorResult("ACCRINTM requires rate to be number argument")
|
return MakeErrorResult("ACCRINTM requires rate to be number argument")
|
||||||
}
|
}
|
||||||
|
@ -1376,8 +1376,8 @@ const (
|
|||||||
// Round is an implementation of the Excel ROUND function that rounds a number
|
// Round is an implementation of the Excel ROUND function that rounds a number
|
||||||
// to a specified number of digits.
|
// to a specified number of digits.
|
||||||
func round(args []Result, mode rmode) Result {
|
func round(args []Result, mode rmode) Result {
|
||||||
if len(args) == 0 {
|
if len(args) != 2 {
|
||||||
return MakeErrorResult("ROUND() requires at least one numeric arguments")
|
return MakeErrorResult("ROUND() requires two numeric arguments")
|
||||||
}
|
}
|
||||||
// number to round
|
// number to round
|
||||||
number := args[0].AsNumber()
|
number := args[0].AsNumber()
|
||||||
@ -1385,14 +1385,11 @@ func round(args []Result, mode rmode) Result {
|
|||||||
return MakeErrorResult("first argument to ROUND() must be a number")
|
return MakeErrorResult("first argument to ROUND() must be a number")
|
||||||
}
|
}
|
||||||
|
|
||||||
digits := float64(0)
|
|
||||||
if len(args) > 1 {
|
|
||||||
digitArg := args[1].AsNumber()
|
digitArg := args[1].AsNumber()
|
||||||
if digitArg.Type != ResultTypeNumber {
|
if digitArg.Type != ResultTypeNumber {
|
||||||
return MakeErrorResult("second argument to ROUND() must be a number")
|
return MakeErrorResult("second argument to ROUND() must be a number")
|
||||||
}
|
}
|
||||||
digits = digitArg.ValueNumber
|
digits := digitArg.ValueNumber
|
||||||
}
|
|
||||||
|
|
||||||
v := number.ValueNumber
|
v := number.ValueNumber
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ func init() {
|
|||||||
RegisterFunction("LEN", Len)
|
RegisterFunction("LEN", Len)
|
||||||
RegisterFunction("LENB", Len)
|
RegisterFunction("LENB", Len)
|
||||||
RegisterFunction("LOWER", Lower)
|
RegisterFunction("LOWER", Lower)
|
||||||
|
RegisterFunction("MID", Mid)
|
||||||
RegisterFunction("PROPER", Proper)
|
RegisterFunction("PROPER", Proper)
|
||||||
RegisterFunction("REPLACE", Replace)
|
RegisterFunction("REPLACE", Replace)
|
||||||
RegisterFunction("REPT", Rept)
|
RegisterFunction("REPT", Rept)
|
||||||
@ -40,6 +41,7 @@ func init() {
|
|||||||
RegisterFunction("RIGHTB", Right)
|
RegisterFunction("RIGHTB", Right)
|
||||||
RegisterFunction("SEARCH", Search)
|
RegisterFunction("SEARCH", Search)
|
||||||
RegisterFunctionComplex("SEARCHB", Searchb)
|
RegisterFunctionComplex("SEARCHB", Searchb)
|
||||||
|
RegisterFunction("SUBSTITUTE", Substitute)
|
||||||
RegisterFunction("T", T)
|
RegisterFunction("T", T)
|
||||||
RegisterFunction("TEXT", Text)
|
RegisterFunction("TEXT", Text)
|
||||||
RegisterFunction("TEXTJOIN", TextJoin)
|
RegisterFunction("TEXTJOIN", TextJoin)
|
||||||
@ -380,6 +382,43 @@ func lower(arg Result) Result {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mid is an implementation of the Excel MID function that returns a copy
|
||||||
|
// of the string with each word capitalized.
|
||||||
|
func Mid(args []Result) Result {
|
||||||
|
if len(args) != 3 {
|
||||||
|
return MakeErrorResult("MID requires three arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeString {
|
||||||
|
return MakeErrorResult("MID requires text to be a string")
|
||||||
|
}
|
||||||
|
text := args[0].ValueString
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("MID requires start_num to be a number")
|
||||||
|
}
|
||||||
|
startNum := int(args[1].ValueNumber)
|
||||||
|
if startNum < 1 {
|
||||||
|
return MakeErrorResult("MID requires start_num to be more than 0")
|
||||||
|
}
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("MID requires num_chars to be a number")
|
||||||
|
}
|
||||||
|
numChars := int(args[2].ValueNumber)
|
||||||
|
if numChars < 0 {
|
||||||
|
return MakeErrorResult("MID requires num_chars to be non negative")
|
||||||
|
}
|
||||||
|
l := len(text)
|
||||||
|
if startNum > l {
|
||||||
|
return MakeStringResult("")
|
||||||
|
}
|
||||||
|
startNum--
|
||||||
|
endNum := startNum + numChars
|
||||||
|
if endNum > l + 1 {
|
||||||
|
return MakeStringResult(text[startNum:])
|
||||||
|
} else {
|
||||||
|
return MakeStringResult(text[startNum:endNum])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Proper is an implementation of the Excel PROPER function that returns a copy
|
// Proper is an implementation of the Excel PROPER function that returns a copy
|
||||||
// of the string with each word capitalized.
|
// of the string with each word capitalized.
|
||||||
func Proper(args []Result) Result {
|
func Proper(args []Result) Result {
|
||||||
@ -534,6 +573,67 @@ func Searchb(ctx Context, ev Evaluator, args []Result) Result {
|
|||||||
return MakeErrorResultType(ErrorTypeValue, "Not found")
|
return MakeErrorResultType(ErrorTypeValue, "Not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Substitute is an implementation of the Excel SUBSTITUTE function.
|
||||||
|
func Substitute(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum != 3 && argsNum != 4 {
|
||||||
|
return MakeErrorResult("SUBSTITUTE requires three or four arguments")
|
||||||
|
}
|
||||||
|
text, errResult := getString(args[0], "SUBSTITUTE", "text")
|
||||||
|
if errResult.Type == ResultTypeError {
|
||||||
|
return errResult
|
||||||
|
}
|
||||||
|
oldText, errResult := getString(args[1], "SUBSTITUTE", "old text")
|
||||||
|
if errResult.Type == ResultTypeError {
|
||||||
|
return errResult
|
||||||
|
}
|
||||||
|
newText, errResult := getString(args[2], "SUBSTITUTE", "new text")
|
||||||
|
if errResult.Type == ResultTypeError {
|
||||||
|
return errResult
|
||||||
|
}
|
||||||
|
instanceNum := 0
|
||||||
|
if argsNum == 3 {
|
||||||
|
return MakeStringResult(strings.Replace(text, oldText, newText, -1))
|
||||||
|
} else {
|
||||||
|
instanceNumF, errResult := getNumber(args[3], "SUBSTITUTE", "instance_num")
|
||||||
|
if errResult.Type == ResultTypeError {
|
||||||
|
return errResult
|
||||||
|
}
|
||||||
|
instanceNum = int(instanceNumF)
|
||||||
|
if instanceNum < 1 {
|
||||||
|
return MakeErrorResult("instance_num should be more than zero")
|
||||||
|
}
|
||||||
|
textCopy := text
|
||||||
|
countdown := instanceNum
|
||||||
|
pos := -1
|
||||||
|
l := len(oldText)
|
||||||
|
thrownTotal := 0
|
||||||
|
for {
|
||||||
|
countdown--
|
||||||
|
index := strings.Index(textCopy, oldText)
|
||||||
|
if index == -1 {
|
||||||
|
pos = -1
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
pos = index + thrownTotal
|
||||||
|
if countdown == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
thrown := l + index
|
||||||
|
thrownTotal += thrown
|
||||||
|
textCopy = textCopy[thrown:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pos == -1 {
|
||||||
|
return MakeStringResult(text)
|
||||||
|
} else {
|
||||||
|
pre := text[:pos]
|
||||||
|
post := text[pos+l:]
|
||||||
|
return MakeStringResult(pre + newText + post)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// T is an implementation of the Excel T function that returns whether the
|
// T is an implementation of the Excel T function that returns whether the
|
||||||
// argument is text.
|
// argument is text.
|
||||||
func T(args []Result) Result {
|
func T(args []Result) Result {
|
||||||
@ -752,3 +852,29 @@ func Text(args []Result) Result {
|
|||||||
return MakeErrorResult("Incorrect argument for TEXT")
|
return MakeErrorResult("Incorrect argument for TEXT")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getString(arg Result, funcName, argName string) (string, Result) {
|
||||||
|
switch arg.Type {
|
||||||
|
case ResultTypeString, ResultTypeNumber, ResultTypeEmpty:
|
||||||
|
return arg.Value(), empty
|
||||||
|
default:
|
||||||
|
return "", MakeErrorResult(funcName + " requires " + argName + " to be a number or string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNumber(arg Result, funcName, argName string) (float64, Result) {
|
||||||
|
switch arg.Type {
|
||||||
|
case ResultTypeEmpty:
|
||||||
|
return 0, empty
|
||||||
|
case ResultTypeNumber:
|
||||||
|
return arg.ValueNumber, empty
|
||||||
|
case ResultTypeString:
|
||||||
|
f, err := strconv.ParseFloat(arg.ValueString, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, MakeErrorResult(argName + " should be a number for " + funcName)
|
||||||
|
}
|
||||||
|
return f, empty
|
||||||
|
default:
|
||||||
|
return 0, MakeErrorResult(funcName + " requires " + argName + " to be a number or empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -564,8 +564,29 @@ func TestMatch(t *testing.T) {
|
|||||||
runTests(t, ctx, td)
|
runTests(t, ctx, td)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMaxA(t *testing.T) {
|
func TestMax(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(0.1)
|
||||||
|
sheet.Cell("B1").SetNumber(0.2)
|
||||||
|
|
||||||
|
sheet.Cell("A2").SetNumber(0.4)
|
||||||
|
sheet.Cell("B2").SetNumber(0.8)
|
||||||
|
|
||||||
|
sheet.Cell("A3").SetBool(true)
|
||||||
|
sheet.Cell("B3").SetBool(false)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`MAX(A1:B3)`, `0.8 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMaxA(t *testing.T) {
|
||||||
ss := spreadsheet.New()
|
ss := spreadsheet.New()
|
||||||
sheet := ss.AddSheet()
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
@ -587,8 +608,29 @@ func TestMaxA(t *testing.T) {
|
|||||||
runTests(t, ctx, td)
|
runTests(t, ctx, td)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMinA(t *testing.T) {
|
func TestMin(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(0.1)
|
||||||
|
sheet.Cell("B1").SetNumber(0.2)
|
||||||
|
|
||||||
|
sheet.Cell("A2").SetNumber(0.4)
|
||||||
|
sheet.Cell("B2").SetNumber(0.8)
|
||||||
|
|
||||||
|
sheet.Cell("A3").SetBool(true)
|
||||||
|
sheet.Cell("B3").SetBool(false)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`MIN(A1:B3)`, `0.1 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMinA(t *testing.T) {
|
||||||
ss := spreadsheet.New()
|
ss := spreadsheet.New()
|
||||||
sheet := ss.AddSheet()
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
@ -611,7 +653,6 @@ func TestMinA(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIfs(t *testing.T) {
|
func TestIfs(t *testing.T) {
|
||||||
|
|
||||||
ss := spreadsheet.New()
|
ss := spreadsheet.New()
|
||||||
sheet := ss.AddSheet()
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
@ -2742,3 +2783,111 @@ func TestYieldmat(t *testing.T) {
|
|||||||
|
|
||||||
runTests(t, ctx, td)
|
runTests(t, ctx, td)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMid(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=MID("Fluid Flow",1,5)`, `Fluid ResultTypeString`},
|
||||||
|
{`=MID("Fluid Flow",7,20)`, `Flow ResultTypeString`},
|
||||||
|
{`=MID("Fluid Flow",20,5)`, ` ResultTypeString`},
|
||||||
|
{`=MID("Fluid Flow",1,0)`, ` ResultTypeString`},
|
||||||
|
{`=MID("Fluid Flow",0,5)`, `#VALUE! ResultTypeError`},
|
||||||
|
{`=MID("Fluid Flow",7,-20)`, `#VALUE! ResultTypeError`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubstitute(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetString("Hello Earth Earth Earth")
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=SUBSTITUTE(A1,"Earth","Krypton",1)`, `Hello Krypton Earth Earth ResultTypeString`},
|
||||||
|
{`=SUBSTITUTE(A1,"Earth","Krypton",2)`, `Hello Earth Krypton Earth ResultTypeString`},
|
||||||
|
{`=SUBSTITUTE(A1,"Earth","Krypton",3)`, `Hello Earth Earth Krypton ResultTypeString`},
|
||||||
|
{`=SUBSTITUTE(A1,"Earth","Krypton",4)`, `Hello Earth Earth Earth ResultTypeString`},
|
||||||
|
{`=SUBSTITUTE(A1,"Earth","Krypton")`, `Hello Krypton Krypton Krypton ResultTypeString`},
|
||||||
|
{`=SUBSTITUTE(A1,"World","Krypton")`, `Hello Earth Earth Earth ResultTypeString`},
|
||||||
|
{`=SUBSTITUTE(A1,"Earth","Krypton",0)`, `#VALUE! ResultTypeError`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnd(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=AND(FALSE,FALSE)`, `0 ResultTypeNumber`},
|
||||||
|
{`=AND(TRUE,FALSE)`, `0 ResultTypeNumber`},
|
||||||
|
{`=AND(FALSE,TRUE)`, `0 ResultTypeNumber`},
|
||||||
|
{`=AND(TRUE,TRUE)`, `1 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIferror(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=IFERROR("No error","ERROR")`, `No error ResultTypeString`},
|
||||||
|
{`=IFERROR(1/0,"ERROR")`, `ERROR ResultTypeString`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChar(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=CHAR(65)`, `A ResultTypeString`},
|
||||||
|
{`=CHAR(255)`, `ÿ ResultTypeString`},
|
||||||
|
{`=CHAR(1000)`, `#VALUE! ResultTypeError`},
|
||||||
|
{`=CHAR("invalid")`, `#VALUE! ResultTypeError`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRound(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=ROUND(2.14,1)`, `2.1 ResultTypeNumber`},
|
||||||
|
{`=ROUND(2.16,1)`, `2.2 ResultTypeNumber`},
|
||||||
|
{`=ROUND(-2.14,1)`, `-2.1 ResultTypeNumber`},
|
||||||
|
{`=ROUND(-2.16,1)`, `-2.2 ResultTypeNumber`},
|
||||||
|
{`=ROUND(21.5,-1)`, `20 ResultTypeNumber`},
|
||||||
|
{`=ROUND(21.5,-2)`, `0 ResultTypeNumber`},
|
||||||
|
{`=ROUND(-55.5,-1)`, `-60 ResultTypeNumber`},
|
||||||
|
{`=ROUND(-55.5,-2)`, `-100 ResultTypeNumber`},
|
||||||
|
{`=ROUND(-55.5,0)`, `-56 ResultTypeNumber`},
|
||||||
|
{`=ROUND(-55.4,)`, `-55 ResultTypeNumber`},
|
||||||
|
{`=ROUND(-55.4)`, `#VALUE! ResultTypeError`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user