| 1 | package calc |
| 2 | |
| 3 | import "testing" |
| 4 | |
| 5 | func TestIfBranches(t *testing.T) { |
| 6 | v := Eval(mustParse(t, `IF(TRUE,"yes","no")`), emptyEnv{}) |
| 7 | if v.Kind != KindStr || v.Str != "yes" { |
| 8 | t.Fatalf("IF(TRUE,...) = %v, want \"yes\"", v) |
| 9 | } |
| 10 | v = Eval(mustParse(t, `IF(FALSE,"yes","no")`), emptyEnv{}) |
| 11 | if v.Kind != KindStr || v.Str != "no" { |
| 12 | t.Fatalf("IF(FALSE,...) = %v, want \"no\"", v) |
| 13 | } |
| 14 | } |
| 15 | |
| 16 | // The untaken branch is still evaluated by evalCall (Func receives |
| 17 | // finished Values, not thunks), but fnIf only ever returns the |
| 18 | // already-computed value it selects — so the discarded branch's own |
| 19 | // error never surfaces in the result. |
| 20 | func TestIfDiscardsErrorInUntakenBranch(t *testing.T) { |
| 21 | v := Eval(mustParse(t, "IF(FALSE,1/0,5)"), emptyEnv{}) |
| 22 | if v.Kind != KindNum || v.Num != 5 { |
| 23 | t.Fatalf("IF(FALSE,1/0,5) = %v, want 5", v) |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | func TestIfsFirstTrueWins(t *testing.T) { |
| 28 | v := Eval(mustParse(t, "IFS(FALSE,1,TRUE,2,TRUE,3)"), emptyEnv{}) |
| 29 | if v.Kind != KindNum || v.Num != 2 { |
| 30 | t.Fatalf("IFS(...) = %v, want 2", v) |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | func TestIfsNoTrueIsNA(t *testing.T) { |
| 35 | v := Eval(mustParse(t, "IFS(FALSE,1,FALSE,2)"), emptyEnv{}) |
| 36 | if v.Kind != KindErr || v.Err != ErrNA { |
| 37 | t.Fatalf("IFS(all false) = %v, want #N/A", v) |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | func TestAndOrNot(t *testing.T) { |
| 42 | v := Eval(mustParse(t, "AND(TRUE,TRUE,TRUE)"), emptyEnv{}) |
| 43 | if v.Kind != KindBool || !v.Bool { |
| 44 | t.Fatalf("AND(T,T,T) = %v, want TRUE", v) |
| 45 | } |
| 46 | v = Eval(mustParse(t, "AND(TRUE,FALSE)"), emptyEnv{}) |
| 47 | if v.Kind != KindBool || v.Bool { |
| 48 | t.Fatalf("AND(T,F) = %v, want FALSE", v) |
| 49 | } |
| 50 | v = Eval(mustParse(t, "OR(FALSE,FALSE,TRUE)"), emptyEnv{}) |
| 51 | if v.Kind != KindBool || !v.Bool { |
| 52 | t.Fatalf("OR(F,F,T) = %v, want TRUE", v) |
| 53 | } |
| 54 | v = Eval(mustParse(t, "NOT(TRUE)"), emptyEnv{}) |
| 55 | if v.Kind != KindBool || v.Bool { |
| 56 | t.Fatalf("NOT(TRUE) = %v, want FALSE", v) |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | func TestAndPropagatesError(t *testing.T) { |
| 61 | env := mapEnv{{1, 1}: {Kind: KindErr, Err: ErrRef}} |
| 62 | v := Eval(mustParse(t, "AND(A1,TRUE)"), env) |
| 63 | if v.Kind != KindErr || v.Err != ErrRef { |
| 64 | t.Fatalf("AND(A1,TRUE) = %v, want #REF!", v) |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | // IFERROR is the one place a formula is allowed to inspect an error |
| 69 | // argument instead of becoming it (evalCall does not short-circuit on an |
| 70 | // error argument for exactly this reason). |
| 71 | func TestIfError(t *testing.T) { |
| 72 | v := Eval(mustParse(t, `IFERROR(1/0,"fallback")`), emptyEnv{}) |
| 73 | if v.Kind != KindStr || v.Str != "fallback" { |
| 74 | t.Fatalf(`IFERROR(1/0,"fallback") = %v, want "fallback"`, v) |
| 75 | } |
| 76 | v = Eval(mustParse(t, `IFERROR(5,"fallback")`), emptyEnv{}) |
| 77 | if v.Kind != KindNum || v.Num != 5 { |
| 78 | t.Fatalf(`IFERROR(5,"fallback") = %v, want 5`, v) |
| 79 | } |
| 80 | } |
| 81 | |