src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/vals/index_test.go (about) 1 package vals 2 3 import ( 4 "os" 5 "testing" 6 7 "src.elv.sh/pkg/eval/errs" 8 "src.elv.sh/pkg/testutil" 9 "src.elv.sh/pkg/tt" 10 ) 11 12 var ( 13 li0 = EmptyList 14 li4 = MakeList("foo", "bar", "lorem", "ipsum") 15 m = MakeMap("foo", "bar", "lorem", "ipsum") 16 ) 17 18 func TestIndex(t *testing.T) { 19 tt.Test(t, Index, 20 // String indices 21 Args("abc", "0").Rets("a", nil), 22 Args("abc", 0).Rets("a", nil), 23 Args("你好", "0").Rets("你", nil), 24 Args("你好", "3").Rets("好", nil), 25 Args("你好", "2").Rets(tt.Any, errIndexNotAtRuneBoundary), 26 // String slices with half-open range. 27 Args("abc", "1..2").Rets("b", nil), 28 Args("abc", "1..").Rets("bc", nil), 29 Args("abc", "..").Rets("abc", nil), 30 Args("abc", "..0").Rets("", nil), // i == j == 0 is allowed 31 Args("abc", "3..").Rets("", nil), // i == j == n is allowed 32 // String slices with closed range. 33 Args("abc", "0..=1").Rets("ab", nil), 34 Args("abc", "1..=").Rets("bc", nil), 35 Args("abc", "..=1").Rets("ab", nil), 36 Args("abc", "..=").Rets("abc", nil), 37 Args("abc", "..=-1").Rets("abc", nil), 38 // String slices not at rune boundary. 39 Args("你好", "2..").Rets(tt.Any, errIndexNotAtRuneBoundary), 40 Args("你好", "..2").Rets(tt.Any, errIndexNotAtRuneBoundary), 41 42 // List indices 43 // ============ 44 45 // Simple indices: 0 <= i < n. 46 Args(li4, "0").Rets("foo", nil), 47 Args(li4, "3").Rets("ipsum", nil), 48 Args(li0, "0").Rets(tt.Any, errs.OutOfRange{ 49 What: "index", ValidLow: "0", ValidHigh: "-1", Actual: "0"}), 50 Args(li4, "4").Rets(tt.Any, errs.OutOfRange{ 51 What: "index", ValidLow: "0", ValidHigh: "3", Actual: "4"}), 52 Args(li4, "5").Rets(tt.Any, errs.OutOfRange{ 53 What: "index", ValidLow: "0", ValidHigh: "3", Actual: "5"}), 54 Args(li4, z).Rets(tt.Any, 55 errs.OutOfRange{What: "index", ValidLow: "0", ValidHigh: "3", Actual: z}), 56 // Negative indices: -n <= i < 0. 57 Args(li4, "-1").Rets("ipsum", nil), 58 Args(li4, "-4").Rets("foo", nil), 59 Args(li4, "-5").Rets(tt.Any, errs.OutOfRange{ 60 What: "negative index", ValidLow: "-4", ValidHigh: "-1", Actual: "-5"}), 61 Args(li4, "-"+z).Rets(tt.Any, 62 errs.OutOfRange{What: "negative index", ValidLow: "-4", ValidHigh: "-1", Actual: "-" + z}), 63 // Float indices are not allowed even if the value is an integer. 64 Args(li4, 0.0).Rets(tt.Any, errIndexMustBeInteger), 65 66 // Integer indices are allowed. 67 Args(li4, 0).Rets("foo", nil), 68 Args(li4, 3).Rets("ipsum", nil), 69 Args(li4, 5).Rets(nil, errs.OutOfRange{ 70 What: "index", ValidLow: "0", ValidHigh: "3", Actual: "5"}), 71 Args(li4, -1).Rets("ipsum", nil), 72 Args(li4, -5).Rets(nil, errs.OutOfRange{ 73 What: "negative index", ValidLow: "-4", ValidHigh: "-1", Actual: "-5"}), 74 75 // Half-open slices. 76 Args(li4, "1..3").Rets(eq(MakeList("bar", "lorem")), nil), 77 Args(li4, "3..4").Rets(eq(MakeList("ipsum")), nil), 78 Args(li4, "0..0").Rets(eq(EmptyList), nil), // i == j == 0 is allowed 79 Args(li4, "4..4").Rets(eq(EmptyList), nil), // i == j == n is allowed 80 // i defaults to 0 81 Args(li4, "..2").Rets(eq(MakeList("foo", "bar")), nil), 82 Args(li4, "..-1").Rets(eq(MakeList("foo", "bar", "lorem")), nil), 83 // j defaults to n 84 Args(li4, "3..").Rets(eq(MakeList("ipsum")), nil), 85 Args(li4, "-2..").Rets(eq(MakeList("lorem", "ipsum")), nil), 86 // Both indices can be omitted. 87 Args(li0, "..").Rets(eq(li0), nil), 88 Args(li4, "..").Rets(eq(li4), nil), 89 90 // Closed slices. 91 Args(li4, "1..=2").Rets(eq(MakeList("bar", "lorem")), nil), 92 Args(li4, "..=1").Rets(eq(MakeList("foo", "bar")), nil), 93 Args(li4, "..=-2").Rets(eq(MakeList("foo", "bar", "lorem")), nil), 94 Args(li4, "3..=").Rets(eq(MakeList("ipsum")), nil), 95 Args(li4, "..=").Rets(eq(li4), nil), 96 Args(li4, "..=-1").Rets(eq(li4), nil), 97 98 // Slice index out of range. 99 Args(li4, "-5..1").Rets(nil, errs.OutOfRange{ 100 What: "negative index", ValidLow: "-4", ValidHigh: "-1", Actual: "-5"}), 101 Args(li4, "0..5").Rets(nil, errs.OutOfRange{ 102 What: "index", ValidLow: "0", ValidHigh: "4", Actual: "5"}), 103 Args(li4, z+"..").Rets(nil, 104 errs.OutOfRange{What: "index", ValidLow: "0", ValidHigh: "4", Actual: z}), 105 // Slice index upper < lower 106 Args(li4, "3..2").Rets(nil, errs.OutOfRange{ 107 What: "slice upper index", ValidLow: "3", ValidHigh: "4", Actual: "2"}), 108 Args(li4, "-1..-2").Rets(nil, 109 errs.OutOfRange{What: "negative slice upper index", 110 ValidLow: "-1", ValidHigh: "-1", Actual: "-2"}), 111 112 // Malformed list indices. 113 Args(li4, "a").Rets(tt.Any, errIndexMustBeInteger), 114 // TODO(xiaq): Make the error more accurate. 115 Args(li4, "1:3:2").Rets(tt.Any, errIndexMustBeInteger), 116 117 // Map indices 118 // ============ 119 120 Args(m, "foo").Rets("bar", nil), 121 Args(m, "bad").Rets(tt.Any, NoSuchKey("bad")), 122 123 // Not indexable 124 Args(1, "foo").Rets(nil, errNotIndexable), 125 ) 126 } 127 128 func TestIndex_File(t *testing.T) { 129 testutil.InTempDir(t) 130 f, err := os.Create("f") 131 if err != nil { 132 t.Skip("create file:", err) 133 } 134 135 tt.Test(t, Index, 136 Args(f, "fd").Rets(int(f.Fd()), nil), 137 Args(f, "name").Rets(f.Name(), nil), 138 Args(f, "x").Rets(nil, NoSuchKey("x")), 139 ) 140 }