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 {
|
||||
return errResult
|
||||
}
|
||||
if issueDate >= settlementDate {
|
||||
return MakeErrorResultType(ErrorTypeNum, "Issue date should be earlier than settlement date")
|
||||
}
|
||||
if args[2].Type != ResultTypeNumber {
|
||||
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
|
||||
// to a specified number of digits.
|
||||
func round(args []Result, mode rmode) Result {
|
||||
if len(args) == 0 {
|
||||
return MakeErrorResult("ROUND() requires at least one numeric arguments")
|
||||
if len(args) != 2 {
|
||||
return MakeErrorResult("ROUND() requires two numeric arguments")
|
||||
}
|
||||
// number to round
|
||||
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")
|
||||
}
|
||||
|
||||
digits := float64(0)
|
||||
if len(args) > 1 {
|
||||
digitArg := args[1].AsNumber()
|
||||
if digitArg.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("second argument to ROUND() must be a number")
|
||||
}
|
||||
digits = digitArg.ValueNumber
|
||||
}
|
||||
digits := digitArg.ValueNumber
|
||||
|
||||
v := number.ValueNumber
|
||||
|
||||
|
@ -33,6 +33,7 @@ func init() {
|
||||
RegisterFunction("LEN", Len)
|
||||
RegisterFunction("LENB", Len)
|
||||
RegisterFunction("LOWER", Lower)
|
||||
RegisterFunction("MID", Mid)
|
||||
RegisterFunction("PROPER", Proper)
|
||||
RegisterFunction("REPLACE", Replace)
|
||||
RegisterFunction("REPT", Rept)
|
||||
@ -40,6 +41,7 @@ func init() {
|
||||
RegisterFunction("RIGHTB", Right)
|
||||
RegisterFunction("SEARCH", Search)
|
||||
RegisterFunctionComplex("SEARCHB", Searchb)
|
||||
RegisterFunction("SUBSTITUTE", Substitute)
|
||||
RegisterFunction("T", T)
|
||||
RegisterFunction("TEXT", Text)
|
||||
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
|
||||
// of the string with each word capitalized.
|
||||
func Proper(args []Result) Result {
|
||||
@ -534,6 +573,67 @@ func Searchb(ctx Context, ev Evaluator, args []Result) Result {
|
||||
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
|
||||
// argument is text.
|
||||
func T(args []Result) Result {
|
||||
@ -752,3 +852,29 @@ func Text(args []Result) Result {
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
sheet := ss.AddSheet()
|
||||
|
||||
@ -587,8 +608,29 @@ func TestMaxA(t *testing.T) {
|
||||
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()
|
||||
sheet := ss.AddSheet()
|
||||
|
||||
@ -611,7 +653,6 @@ func TestMinA(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIfs(t *testing.T) {
|
||||
|
||||
ss := spreadsheet.New()
|
||||
sheet := ss.AddSheet()
|
||||
|
||||
@ -2742,3 +2783,111 @@ func TestYieldmat(t *testing.T) {
|
||||
|
||||
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