1package formula23import (4 "strings"5 "testing"6)78func TestScanCaret(t *testing.T) {9 for _, tc := range []struct {10 text string // | marks the caret11 word string12 fn string13 arg int14 }{15 {"=SU|", "SU", "", 0},16 {"=@su|", "su", "", 0},17 {"=SUM(|", "", "SUM", 0},18 {"=SUM(A1, B|", "B", "SUM", 1},19 {"=IF(A1>2, SUM(B1; B2), |", "", "IF", 2},20 {"=IF(A1, (1+|", "", "IF", 1},21 {`=IF(A1="a,b(", |`, "", "IF", 1},22 {`=CONCAT("SU|`, "", "", 0},23 {"=SUM (1, |", "", "SUM", 1},24 {"=jev.te|", "jev.te", "", 0},25 {"=A1+1|", "", "", 0},26 {"=SU|M(1)", "", "", 0},27 {"=Sales|", "Sales", "", 0},28 // Sheet names: quoted ones are a word from the quote on, and29 // what's inside the quotes doesn't open calls or arguments.30 {"=SUM('Q3 p|", "'Q3 p", "SUM", 0},31 {"='|", "'", "", 0},32 {"=SUM('a(b, c'!A1, |", "", "SUM", 1},33 {"='Q3 plan'!A|", "A", "", 0},34 {"=Summary!B|", "B", "", 0},35 } {36 i := strings.Index(tc.text, "|")37 buf := []rune(strings.Replace(tc.text, "|", "", 1))38 c := ScanCaret(buf, len([]rune(tc.text[:i])))39 if c.Word != tc.word || c.Fn != tc.fn || c.Arg != tc.arg {40 t.Errorf("%s: word %q fn %q arg %d", tc.text, c.Word, c.Fn, c.Arg)41 }42 }43}4445// In a structured reference's brackets the word is the item being46// typed, spaces, commas and escapes included, and calls outside it still47// count.48func TestScanCaretInTable(t *testing.T) {49 for _, tc := range []struct {50 text, table, word, fn string51 }{52 {"=SUM(Sales[|", "Sales", "", "SUM"},53 {"=SUM(Sales[Unit P|", "Sales", "Unit P", "SUM"},54 {"=Sales[@Am|", "Sales", "@Am", ""},55 {"=Sales[[#Headers],[To|", "Sales", "To", ""},56 {"=Sales[[#Headers], |", "Sales", " ", ""},57 {"=Sales['#it|", "Sales", "'#it", ""},58 {"=Sales[x]+Co|", "", "Co", ""},59 {"=SUM(Sales[[a]:[b]], |", "", "", "SUM"},60 } {61 i := strings.Index(tc.text, "|")62 buf := []rune(strings.Replace(tc.text, "|", "", 1))63 c := ScanCaret(buf, len([]rune(tc.text[:i])))64 if c.Table != tc.table || c.Word != tc.word || c.Fn != tc.fn {65 t.Errorf("%s: table %q word %q fn %q", tc.text, c.Table, c.Word, c.Fn)66 }67 }68}6970func TestArgPart(t *testing.T) {71 for _, tc := range []struct {72 args string73 arg int74 variadic bool75 want string76 }{77 {"value1, [value2, ...]", 0, true, "value1"},78 {"value1, [value2, ...]", 4, true, "[value2, ...]"},79 {"condition, value_if_true, [value_if_false]", 2, false, "[value_if_false]"},80 {"condition, value_if_true, [value_if_false]", 3, false, ""},81 {"sum_range, criteria_range1, criterion1, [criteria_range2, criterion2, ...]", 5, true, "[criteria_range2, criterion2, ...]"},82 } {83 parts := SplitArgs(tc.args)84 got := ""85 if i := ArgPart(parts, tc.arg, tc.variadic); i >= 0 {86 got = parts[i]87 }88 if got != tc.want {89 t.Errorf("%s arg %d: %q, want %q", tc.args, tc.arg, got, tc.want)90 }91 }92}9394func TestCycleRef(t *testing.T) {95 tests := []struct {96 in string97 caret int98 want string99 ok bool100 }{101 {"=a1", 3, "=$A$1", true},102 {"=A1+1", 1, "=$A$1+1", true},103 {"=SUM(A1:B2)", 10, "=SUM($A$1:$B$2)", true},104 {"=SUM(A1:B2)", 7, "=SUM($A$1:$B$2)", true},105 {"=SUM($A$1..B2)", 12, "=SUM(A$1..B$2)", true},106 {`="A1"`, 3, "", false},107 {"=LOG10(2)", 6, "", false},108 {"=1+2", 2, "", false},109 }110 for _, tt := range tests {111 out, pos, ok := CycleRef([]rune(tt.in), tt.caret)112 if ok != tt.ok || string(out) != tt.want {113 t.Errorf("CycleRef(%q, %d) = %q, %v; want %q", tt.in, tt.caret, string(out), ok, tt.want)114 }115 if ok && pos > len(out) {116 t.Errorf("caret %d past end of %q", pos, string(out))117 }118 }119}