1// SPDX-License-Identifier: MIT
2
3package expr
4
5import (
6 "os"
7 "strings"
8 "testing"
9 "unicode/utf16"
10)
11
12// TestOrdinalFoldMatchesRunner replays a few hundred strings through .NET's
13// StringComparison.OrdinalIgnoreCase, recorded by `runner.dll --cmp` (see
14// gen/runner), and checks equality, ordering, contains, startsWith and
15// endsWith. The cases are chosen around what differs from JavaScript: ß, ʼn,
16// ı, İ, ſ, the Kelvin and Angstrom signs, final sigma, titlecase digraphs,
17// supplementary letters, and code points on both sides of U+E000.
18func TestOrdinalFoldMatchesRunner(t *testing.T) {
19 // "a b want" where a and b are dot-separated UTF-16 code units in hex and
20 // want is "equal order contains startsWith endsWith" as printed by --cmp.
21 data, err := os.ReadFile("testdata/ordinal_cases.txt")
22 if err != nil {
23 t.Fatal(err)
24 }
25 decode := func(h string) string {
26 if h == "-" {
27 return ""
28 }
29 var units []uint16
30 for _, u := range strings.Split(h, ".") {
31 var n uint16
32 for _, c := range u {
33 n = n<<4 | uint16(strings.IndexRune("0123456789ABCDEF", c))
34 }
35 units = append(units, n)
36 }
37 return utf16String(units)
38 }
39 n := 0
40 for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
41 f := strings.Fields(line)
42 if len(f) != 7 {
43 t.Fatalf("bad line %q", line)
44 }
45 a, b := decode(f[0]), decode(f[1])
46 want := strings.Join(f[2:], " ")
47 b2i := func(v bool) string {
48 if v {
49 return "1"
50 }
51 return "0"
52 }
53 fa, fb := ordinalFold(a), ordinalFold(b)
54 got := strings.Join([]string{
55 b2i(ordinalEqual(a, b)),
56 map[int]string{-1: "-1", 0: "0", 1: "1"}[ordinalCompare(a, b)],
57 b2i(jsIncludes(fa, fb)),
58 b2i(jsHasPrefix(fa, fb)),
59 b2i(jsHasSuffix(fa, fb)),
60 }, " ")
61 if got != want {
62 t.Errorf("%q vs %q (%s %s): got %s, runner %s", a, b, f[0], f[1], got, want)
63 }
64 n++
65 }
66 if n < 300 {
67 t.Errorf("only %d cases", n)
68 }
69}
70
71// utf16String decodes UTF-16 code units. The cases here hold only paired
72// surrogates.
73func utf16String(units []uint16) string { return string(utf16.Decode(units)) }