package gotext import ( "bytes" "fmt" "os" "reflect" ) func showColour() bool { return os.Getenv("NO_COLOUR") == "" } func testStart(name string) { fmt.Fprintf(os.Stderr, "%s:\n", name) } func testing(message string, body func()) { if showColour() { fmt.Fprintf( os.Stderr, "\033[0;33mtesting\033[0m: %s... ", message, ) body() fmt.Fprintf(os.Stderr, "\033[0;32mOK\033[0m.\n") } else { fmt.Fprintf(os.Stderr, "testing: %s... ", message) body() fmt.Fprintf(os.Stderr, "OK.\n") } } func assertEq(given any, expected any) { if !reflect.DeepEqual(given, expected) { if showColour() { fmt.Fprintf(os.Stderr, "\033[0;31mERR\033[0m.\n") } else { fmt.Fprintf(os.Stderr, "ERR.\n") } fmt.Fprintf(os.Stderr, "given != expected\n") fmt.Fprintf(os.Stderr, "given: %#v\n", given) fmt.Fprintf(os.Stderr, "expected: %#v\n", expected) os.Exit(1) } } func src(payload string) string { f, err := os.CreateTemp("", "gotext-temp-*.go") assertEq(err, nil) _, err = f.WriteString(payload) assertEq(err, nil) err = f.Close() assertEq(err, nil) return f.Name() } func test_formatComment() { testStart("formatComment()") testing("formatting comments FIXME", func() { table := []struct{ in string out string }{ {"// foo ", "#. foo\n"}, {"/* foo */", "#. foo\n"}, {"/* foo\n */", "#. foo\n"}, {"/* foo\nbar */", "#. foo\n#. bar\n"}, } for _, row := range table { assertEq(formatComment(row.in), row.out) } }) } func test_processFiles() { testStart("processFiles()") testing("simple commented file", func() { const s = `package main func main() { // TRANSLATORS: a comment i18n.G("foo") } ` fname := src(s) defer os.Remove(fname) msgIDs, err := processFiles(argsT{ subArgs: []string{fname}, keyword: "i18n.G", }) assertEq(err, nil) assertEq(msgIDs, map[string][]msgIDT{ "foo": []msgIDT{ msgIDT{ comment: "#. TRANSLATORS: a comment\n", fname: fname, line: 5, }, }, }) }) testing("process multiple entries", func() { const s = `package main func main() { // TRANSLATORS: comment 1 i18n.G("foo") // TRANSLATORS: comment 2 i18n.G("foo") } ` fname := src(s) defer os.Remove(fname) msgIDs, err := processFiles(argsT{ subArgs: []string{fname}, keyword: "i18n.G", }) assertEq(err, nil) assertEq(msgIDs, map[string][]msgIDT{ "foo": []msgIDT{ { comment: "#. TRANSLATORS: comment 1\n", fname: fname, line: 5, }, { comment: "#. TRANSLATORS: comment 2\n", fname: fname, line: 8, }, }, }) }) testing("process files with concat", func() { const s = `package main func main() { // TRANSLATORS: a comment i18n.G("foo\n" + "bar\n" + "baz") } ` fname := src(s) defer os.Remove(fname) msgIDs, err := processFiles(argsT{ subArgs: []string{fname}, keyword: "i18n.G", }) assertEq(err, nil) assertEq(msgIDs, map[string][]msgIDT{ "foo\\nbar\\nbaz": []msgIDT{ { comment: "#. TRANSLATORS: a comment\n", fname: fname, line: 5, }, }, }) }) testing("file with quote", func() { const s = `package main func main() { i18n.G(%[1]s foo "bar"%[1]s) } ` fname := src(fmt.Sprintf(s, "`")) defer os.Remove(fname) msgIDs, err := processFiles(argsT{ subArgs: []string{fname}, keyword: "i18n.G", }) assertEq(err, nil) out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expectedRaw = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: %s:4 msgid " foo \"bar\"" msgstr "" ` expected := fmt.Sprintf(expectedRaw, fname) assertEq(out.String(), expected) }) } func test_writePotFile() { testStart("writePotFile()") testing("write simple file", func() { msgIDs := map[string][]msgIDT{ "foo": []msgIDT{ { fname: "fname", line: 2, comment: "#. foo\n", }, }, } out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expected = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. foo #: fname:2 msgid "foo" msgstr "" ` assertEq(out.String(), expected) }) testing("write template with 2 entries", func() { msgIDs := map[string][]msgIDT{ "foo": []msgIDT{ { fname: "fname", line: 2, comment: "#. comment1\n", }, { fname: "fname", line: 4, comment: "#. comment2\n", }, }, } out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expected = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. comment1 #. comment2 #: fname:2 fname:4 msgid "foo" msgstr "" ` assertEq(out.String(), expected) }) testing("template without comment", func() { msgIDs := map[string][]msgIDT{ "foo": []msgIDT{ { fname: "fname", line: 2, }, }, } out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expected = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: fname:2 msgid "foo" msgstr "" ` assertEq(out.String(), expected) }) testing("output without location", func() { msgIDs := map[string][]msgIDT{ "foo": []msgIDT{ { fname: "fname", line: 2, }, }, } out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expected = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: fname:2 msgid "foo" msgstr "" ` assertEq(out.String(), expected) }) testing("output with hint", func() { msgIDs := map[string][]msgIDT{ "foo": []msgIDT{ { fname: "fname", line: 2, formatHint: "c-format", }, }, } out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expected = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: fname:2 #, c-format msgid "foo" msgstr "" ` assertEq(out.String(), expected) }) testing("output with plural", func() { msgIDs := map[string][]msgIDT{ "foo": []msgIDT{ { msgidPlural: "plural", fname: "fname", line: 2, }, }, } out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expected = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: fname:2 msgid "foo" msgid_plural "plural" msgstr[0] "" msgstr[1] "" ` assertEq(out.String(), expected) }) testing("multiline output", func() { msgIDs := map[string][]msgIDT{ "foo\\nbar\\nbaz": []msgIDT{ { fname: "fname", line: 2, comment: "#. foo\n", }, }, } out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expected = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. foo #: fname:2 msgid "" "foo\n" "bar\n" "baz" msgstr "" ` assertEq(out.String(), expected) }) testing("output tidy output", func() { msgIDs := map[string][]msgIDT{ "foo\\nbar\\nbaz": []msgIDT{ { fname: "fname", line: 2, }, }, "zzz\\n": []msgIDT{ { fname: "fname", line: 4, }, }, } out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expected = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: fname:2 msgid "" "foo\n" "bar\n" "baz" msgstr "" #: fname:4 msgid "zzz\n" msgstr "" ` assertEq(out.String(), expected) }) testing("source with double quotes", func() { const s = `package main func main() { i18n.G("foo \"bar\"") } ` fname := src(s) defer os.Remove(fname) msgIDs, err := processFiles(argsT{ subArgs: []string{fname}, keyword: "i18n.G", }) assertEq(err, nil) out := bytes.NewBuffer([]byte("")) writePotFile(out, msgIDs) const expectedRaw = `msgid "" msgstr "" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: %[1]s:4 msgid "foo \"bar\"" msgstr "" ` expected := fmt.Sprintf(expectedRaw, fname) assertEq(out.String(), expected) }) } func MainTest() { test_formatComment() test_processFiles() test_writePotFile() }