package tre import ( "fmt" "os" "testing" "testing/internal/testdeps" ) func TestGenCharBlocksWellFormed(t *testing.T) { cBlk := func(from []byte, to []byte) *CharBlock { return &CharBlock{ From: from, To: to, } } seq := func(b ...byte) []byte { return b } tests := []struct { from rune to rune blocks []*CharBlock }{ { from: '\u0000', to: '\u007f', blocks: []*CharBlock{ cBlk(seq(0x00), seq(0x7f)), }, }, { from: '\u0080', to: '\u07ff', blocks: []*CharBlock{ cBlk(seq(0xc2, 0x80), seq(0xdf, 0xbf)), }, }, { from: '\u0800', to: '\u0fff', blocks: []*CharBlock{ cBlk( seq(0xe0, 0xa0, 0x80), seq(0xe0, 0xbf, 0xbf), ), }, }, { from: '\u1000', to: '\ucfff', blocks: []*CharBlock{ cBlk( seq(0xe1, 0x80, 0x80), seq(0xec, 0xbf, 0xbf), ), }, }, { from: '\ud000', to: '\ud7ff', blocks: []*CharBlock{ cBlk( seq(0xed, 0x80, 0x80), seq(0xed, 0x9f, 0xbf), ), }, }, { from: '\ue000', to: '\uffff', blocks: []*CharBlock{ cBlk( seq(0xee, 0x80, 0x80), seq(0xef, 0xbf, 0xbf), ), }, }, { from: '\U00010000', to: '\U0003ffff', blocks: []*CharBlock{ cBlk( seq(0xf0, 0x90, 0x80, 0x80), seq(0xf0, 0xbf, 0xbf, 0xbf), ), }, }, { from: '\U00040000', to: '\U000fffff', blocks: []*CharBlock{ cBlk( seq(0xf1, 0x80, 0x80, 0x80), seq(0xf3, 0xbf, 0xbf, 0xbf), ), }, }, { from: '\U00100000', to: '\U0010ffff', blocks: []*CharBlock{ cBlk( seq(0xf4, 0x80, 0x80, 0x80), seq(0xf4, 0x8f, 0xbf, 0xbf), ), }, }, { from: '\u0000', to: '\U0010ffff', blocks: []*CharBlock{ cBlk(seq(0x00), seq(0x7f)), cBlk(seq(0xc2, 0x80), seq(0xdf, 0xbf)), cBlk( seq(0xe0, 0xa0, 0x80), seq(0xe0, 0xbf, 0xbf), ), cBlk( seq(0xe1, 0x80, 0x80), seq(0xec, 0xbf, 0xbf), ), cBlk( seq(0xed, 0x80, 0x80), seq(0xed, 0x9f, 0xbf), ), cBlk( seq(0xee, 0x80, 0x80), seq(0xef, 0xbf, 0xbf), ), cBlk( seq(0xf0, 0x90, 0x80, 0x80), seq(0xf0, 0xbf, 0xbf, 0xbf), ), cBlk( seq(0xf1, 0x80, 0x80, 0x80), seq(0xf3, 0xbf, 0xbf, 0xbf), ), cBlk( seq(0xf4, 0x80, 0x80, 0x80), seq(0xf4, 0x8f, 0xbf, 0xbf), ), }, }, } for _, tt := range tests { const errmsg = "unexpected character block: want: %+v, got: %+v" tts := fmt.Sprintf("%v..%v", tt.from, tt.to) t.Run(tts, func(t *testing.T) { blks, err := GenCharBlocks(tt.from, tt.to) if err != nil { t.Fatal(err) } if len(blks) != len(tt.blocks) { t.Fatalf(errmsg, tt.blocks, blks) } for i, blk := range blks { expected := tt.blocks[i] neqFrom := len(blk.From) != len(expected.From) neqTo := len(blk.To) != len(expected.To) if neqFrom || neqTo { t.Fatalf(errmsg, tt.blocks, blks) } for j := 0; j < len(blk.From); j++ { neqFrom := blk.From[j] != expected.From[j] neqTo := blk.To[j] != expected.To[j] if neqFrom || neqTo { t.Fatalf( errmsg, tt.blocks, blks, ) } } } }) } } func TestGenCharBlocksIllFormed(t *testing.T) { tests := []struct { from rune to rune }{ { // from > to from: '\u0001', to: '\u0000', }, { from: -1, // U+10FFFF to: '\u0000', }, { from: '\u0000', to: 0x110000, // >U+10FFFF }, { from: 0xd800, // U+D800 (surrogate code point) to: '\ue000', }, { from: 0xdfff, // U+DFFF (surrogate code point) to: '\ue000', }, { from: '\ucfff', to: 0xd800, // U+D800 (surrogate code point) }, { from: '\ucfff', to: 0xdfff, // U+DFFF (surrogate code point) }, } for _, tt := range tests { tts := fmt.Sprintf("%v..%v", tt.from, tt.to) t.Run(tts, func(t *testing.T) { blks, err := GenCharBlocks(tt.from, tt.to) if err == nil { t.Fatal("expected error didn't occur") } if blks != nil { t.Fatal("character blocks must be nil") } }) } } func TestCompressor_Compress(t *testing.T) { x := 0 // an empty value allCompressors := func() []Compressor { return []Compressor{ NewCompressorUniqueEntriesTable(), NewCompressorRowDisplacementTable(x), } } tests := []struct { original []int rowCount int colCount int compressors []Compressor }{ { original: []int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, rowCount: 3, colCount: 5, compressors: allCompressors(), }, { original: []int{ x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, }, rowCount: 3, colCount: 5, compressors: allCompressors(), }, { original: []int{ 1, 1, 1, 1, 1, x, x, x, x, x, 1, 1, 1, 1, 1, }, rowCount: 3, colCount: 5, compressors: allCompressors(), }, { original: []int{ 1, x, 1, 1, 1, 1, 1, x, 1, 1, 1, 1, 1, x, 1, }, rowCount: 3, colCount: 5, compressors: allCompressors(), }, } for i, tt := range tests { for _, comp := range tt.compressors { t.Run(fmt.Sprintf("%T #%v", comp, i), func(t *testing.T) { dup := make([]int, len(tt.original)) copy(dup, tt.original) orig, err := NewOriginalTable(tt.original, tt.colCount) if err != nil { t.Fatal(err) } err = comp.Compress(orig) if err != nil { t.Fatal(err) } rowCount, colCount := comp.OriginalTableSize() if rowCount != tt.rowCount || colCount != tt.colCount { t.Fatalf("unexpected table size; want: %vx%v, got: %vx%v", tt.rowCount, tt.colCount, rowCount, colCount) } for i := 0; i < tt.rowCount; i++ { for j := 0; j < tt.colCount; j++ { v, err := comp.Lookup(i, j) if err != nil { t.Fatal(err) } expected := tt.original[i*tt.colCount+j] if v != expected { t.Fatalf("unexpected entry (%v, %v); want: %v, got: %v", i, j, expected, v) } } } // Calling with out-of-range indexes should be an error. if _, err := comp.Lookup(0, -1); err == nil { t.Fatalf("expected error didn't occur (0, -1)") } if _, err := comp.Lookup(-1, 0); err == nil { t.Fatalf("expected error didn't occur (-1, 0)") } if _, err := comp.Lookup(rowCount-1, colCount); err == nil { t.Fatalf("expected error didn't occur (%v, %v)", rowCount-1, colCount) } if _, err := comp.Lookup(rowCount, colCount-1); err == nil { t.Fatalf("expected error didn't occur (%v, %v)", rowCount, colCount-1) } // The compressor must not break the original table. for i := 0; i < tt.rowCount; i++ { for j := 0; j < tt.colCount; j++ { idx := i*tt.colCount + j if tt.original[idx] != dup[idx] { t.Fatalf("the original table is broken (%v, %v); want: %v, got: %v", i, j, dup[idx], tt.original[idx]) } } } }) } } } var idTests = []struct { id string invalid bool }{ { id: "foo", }, { id: "foo2", }, { id: "foo_bar_baz", }, { id: "f_o_o", }, { id: "Foo", invalid: true, }, { id: "foo_Bar", invalid: true, }, { id: "2foo", invalid: true, }, { id: "_foo", invalid: true, }, { id: "foo_", invalid: true, }, { id: "foo__bar", invalid: true, }, } func TestValidateIdentifier(t *testing.T) { for _, tt := range idTests { t.Run(tt.id, func(t *testing.T) { err := validateIdentifier(tt.id) if tt.invalid { if err == nil { t.Errorf("expected error didn't occur") } } else { if err != nil { t.Errorf("unexpected error occurred: %v", err) } } }) } } func TestLexKindName_validate(t *testing.T) { for _, tt := range idTests { t.Run(tt.id, func(t *testing.T) { err := LexKindName(tt.id).validate() if tt.invalid { if err == nil { t.Errorf("expected error didn't occur") } } else { if err != nil { t.Errorf("unexpected error occurred: %v", err) } } }) } } func TestLexModeName_validate(t *testing.T) { for _, tt := range idTests { t.Run(tt.id, func(t *testing.T) { err := LexModeName(tt.id).validate() if tt.invalid { if err == nil { t.Errorf("expected error didn't occur") } } else { if err != nil { t.Errorf("unexpected error occurred: %v", err) } } }) } } func TestSnakeCaseToUpperCamelCase(t *testing.T) { tests := []struct { snake string camel string }{ { snake: "foo", camel: "Foo", }, { snake: "foo_bar", camel: "FooBar", }, { snake: "foo_bar_baz", camel: "FooBarBaz", }, { snake: "Foo", camel: "Foo", }, { snake: "fooBar", camel: "FooBar", }, { snake: "FOO", camel: "FOO", }, { snake: "FOO_BAR", camel: "FOOBAR", }, { snake: "_foo_bar_", camel: "FooBar", }, { snake: "___foo___bar___", camel: "FooBar", }, } for _, tt := range tests { c := SnakeCaseToUpperCamelCase(tt.snake) if c != tt.camel { t.Errorf("unexpected string; want: %v, got: %v", tt.camel, c) } } } func TestFindSpellingInconsistencies(t *testing.T) { tests := []struct { ids []string duplicated [][]string }{ { ids: []string{"foo", "foo"}, duplicated: nil, }, { ids: []string{"foo", "Foo"}, duplicated: [][]string{{"Foo", "foo"}}, }, { ids: []string{"foo", "foo", "Foo"}, duplicated: [][]string{{"Foo", "foo"}}, }, { ids: []string{"foo_bar_baz", "FooBarBaz"}, duplicated: [][]string{{"FooBarBaz", "foo_bar_baz"}}, }, { ids: []string{"foo", "Foo", "bar", "Bar"}, duplicated: [][]string{{"Bar", "bar"}, {"Foo", "foo"}}, }, { ids: []string{"foo", "Foo", "bar", "Bar", "baz", "bra"}, duplicated: [][]string{{"Bar", "bar"}, {"Foo", "foo"}}, }, } for i, tt := range tests { t.Run(fmt.Sprintf("#%v", i), func(t *testing.T) { duplicated := FindSpellingInconsistencies(tt.ids) if len(duplicated) != len(tt.duplicated) { t.Fatalf("unexpected IDs; want: %#v, got: %#v", tt.duplicated, duplicated) } for i, dupIDs := range duplicated { if len(dupIDs) != len(tt.duplicated[i]) { t.Fatalf("unexpected IDs; want: %#v, got: %#v", tt.duplicated[i], dupIDs) } for j, id := range dupIDs { if id != tt.duplicated[i][j] { t.Fatalf("unexpected IDs; want: %#v, got: %#v", tt.duplicated[i], dupIDs) } } } }) } } func TestLexSpec_Validate(t *testing.T) { // We expect that the spelling inconsistency error will occur. spec := &LexSpec{ Entries: []*LexEntry{ { Modes: []LexModeName{ // 'Default' is the spelling inconsistency because 'default' is predefined. "Default", }, Kind: "foo", Pattern: "foo", }, }, } err := spec.Validate() if err == nil { t.Fatalf("expected error didn't occur") } } func MainTest() { tests := []testing.InternalTest{ { "TestGenCharBlocksWellFormed", TestGenCharBlocksWellFormed }, { "TestGenCharBlocksIllFormed", TestGenCharBlocksIllFormed }, { "TestCompressor_Compress", TestCompressor_Compress }, { "TestValidateIdentifier", TestValidateIdentifier }, { "TestLexKindName_validate", TestLexKindName_validate }, { "TestLexModeName_validate", TestLexModeName_validate }, { "TestSnakeCaseToUpperCamelCase", TestSnakeCaseToUpperCamelCase }, { "TestFindSpellingInconsistencies", TestFindSpellingInconsistencies }, { "TestLexSpec_Validate", TestLexSpec_Validate }, } deps := testdeps.TestDeps{} benchmarks := []testing.InternalBenchmark {} fuzzTargets := []testing.InternalFuzzTarget{} examples := []testing.InternalExample {} m := testing.MainStart(deps, tests, benchmarks, fuzzTargets, examples) os.Exit(m.Run()) }