From 6ff0cc0b400524232cdce44f5b2a68b399ba4adc Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Sat, 1 Apr 2023 09:06:45 +1100 Subject: [PATCH] Enforce example names in documentation --- core/strings/builder.odin | 16 +- core/strings/conversion.odin | 18 +- core/strings/strings.odin | 94 +++++----- tests/documentation/documentation_tester.odin | 163 ++++++++++-------- 4 files changed, 159 insertions(+), 132 deletions(-) diff --git a/core/strings/builder.odin b/core/strings/builder.odin index b4456e236..4994230a9 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -227,7 +227,7 @@ Example: import "core:fmt" import "core:strings" - strings_builder_from_bytes_example :: proc() { + builder_from_bytes_example :: proc() { bytes: [8]byte // <-- gets filled builder := strings.builder_from_bytes(bytes[:]) strings.write_byte(&builder, 'a') @@ -318,7 +318,7 @@ Example: import "core:fmt" import "core:strings" - strings_write_byte_example :: proc() { + write_byte_example :: proc() { builder := strings.builder_make() strings.write_byte(&builder, 'a') // 1 strings.write_byte(&builder, 'b') // 1 @@ -352,7 +352,7 @@ Example: import "core:fmt" import "core:strings" - strings_write_bytes_example :: proc() { + write_bytes_example :: proc() { builder := strings.builder_make() bytes := [?]byte { 'a', 'b', 'c' } strings.write_bytes(&builder, bytes[:]) // 3 @@ -382,7 +382,7 @@ Example: import "core:fmt" import "core:strings" - strings_write_rune_example :: proc() { + write_rune_example :: proc() { builder := strings.builder_make() strings.write_rune(&builder, 'ä') // 2 None strings.write_rune(&builder, 'b') // 1 None @@ -413,7 +413,7 @@ Example: import "core:fmt" import "core:strings" - strings_write_quoted_rune_example :: proc() { + write_quoted_rune_example :: proc() { builder := strings.builder_make() strings.write_string(&builder, "abc") // 3 strings.write_quoted_rune(&builder, 'ä') // 4 @@ -445,7 +445,7 @@ Example: import "core:fmt" import "core:strings" - strings_write_string_example :: proc() { + write_string_example :: proc() { builder := strings.builder_make() strings.write_string(&builder, "a") // 1 strings.write_string(&builder, "bc") // 2 @@ -518,7 +518,7 @@ Example: import "core:fmt" import "core:strings" - strings_write_quoted_string_example :: proc() { + write_quoted_string_example :: proc() { builder := strings.builder_make() strings.write_quoted_string(&builder, "a") // 3 strings.write_quoted_string(&builder, "bc", '\'') // 4 @@ -659,7 +659,7 @@ Example: import "core:fmt" import "core:strings" - strings_write_f32_example :: proc() { + write_f32_example :: proc() { builder := strings.builder_make() strings.write_f32(&builder, 3.14159, 'f') // 6 strings.write_string(&builder, " - ") // 3 diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index c3cf62340..dc41a02dc 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -86,7 +86,7 @@ Example: import "core:fmt" import "core:strings" - strings_to_lower_example :: proc() { + to_lower_example :: proc() { fmt.println(strings.to_lower("TeST")) } @@ -119,7 +119,7 @@ Example: import "core:fmt" import "core:strings" - strings_to_upper_example :: proc() { + to_upper_example :: proc() { fmt.println(strings.to_upper("Test")) } @@ -196,7 +196,7 @@ Example: import "core:strings" import "core:io" - strings_string_case_iterator_example :: proc() { + string_case_iterator_example :: proc() { my_callback :: proc(w: io.Writer, prev, curr, next: rune) { fmt.println("my_callback", curr) // <-- Custom logic here } @@ -325,7 +325,7 @@ Example: import "core:fmt" import "core:strings" - strings_to_delimiter_case_example :: proc() { + to_delimiter_case_example :: proc() { fmt.println(strings.to_delimiter_case("Hello World", '_', false)) fmt.println(strings.to_delimiter_case("Hello World", ' ', true)) fmt.println(strings.to_delimiter_case("aBC", '_', false)) @@ -397,7 +397,7 @@ Example: import "core:fmt" import "core:strings" - strings_to_snake_case_example :: proc() { + to_snake_case_example :: proc() { fmt.println(strings.to_snake_case("HelloWorld")) fmt.println(strings.to_snake_case("Hello World")) } @@ -430,7 +430,7 @@ Example: import "core:fmt" import "core:strings" - strings_to_upper_snake_case_example :: proc() { + to_upper_snake_case_example :: proc() { fmt.println(strings.to_upper_snake_case("HelloWorld")) } @@ -458,7 +458,7 @@ Example: import "core:fmt" import "core:strings" - strings_to_kebab_case_example :: proc() { + to_kebab_case_example :: proc() { fmt.println(strings.to_kebab_case("HelloWorld")) } @@ -486,7 +486,7 @@ Example: import "core:fmt" import "core:strings" - strings_to_upper_kebab_case_example :: proc() { + to_upper_kebab_case_example :: proc() { fmt.println(strings.to_upper_kebab_case("HelloWorld")) } @@ -514,7 +514,7 @@ Example: import "core:fmt" import "core:strings" - strings_to_ada_case_example :: proc() { + to_ada_case_example :: proc() { fmt.println(strings.to_ada_case("HelloWorld")) } diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 64a063b7d..8cb046bd6 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -286,7 +286,7 @@ Example: import "core:fmt" import "core:strings" - strings_contains_example :: proc() { + contains_example :: proc() { fmt.println(strings.contains("testing", "test")) fmt.println(strings.contains("testing", "ing")) fmt.println(strings.contains("testing", "text")) @@ -316,7 +316,7 @@ Example: import "core:fmt" import "core:strings" - strings_contains_any_example :: proc() { + contains_any_example :: proc() { fmt.println(strings.contains_any("test", "test")) fmt.println(strings.contains_any("test", "ts")) fmt.println(strings.contains_any("test", "et")) @@ -347,7 +347,7 @@ Example: import "core:fmt" import "core:strings" - strings_rune_count_example :: proc() { + rune_count_example :: proc() { fmt.println(strings.rune_count("test")) fmt.println(strings.rune_count("testö")) // where len("testö") == 6 } @@ -376,7 +376,7 @@ Example: import "core:fmt" import "core:strings" - strings_equal_fold_example :: proc() { + equal_fold_example :: proc() { fmt.println(strings.equal_fold("test", "test")) fmt.println(strings.equal_fold("Test", "test")) fmt.println(strings.equal_fold("Test", "tEsT")) @@ -447,7 +447,7 @@ Example: import "core:fmt" import "core:strings" - strings_prefix_length_example :: proc() { + prefix_length_example :: proc() { fmt.println(strings.prefix_length("testing", "test")) fmt.println(strings.prefix_length("testing", "te")) fmt.println(strings.prefix_length("telephone", "te")) @@ -499,7 +499,7 @@ Example: import "core:fmt" import "core:strings" - strings_has_prefix_example :: proc() { + has_prefix_example :: proc() { fmt.println(strings.has_prefix("testing", "test")) fmt.println(strings.has_prefix("testing", "te")) fmt.println(strings.has_prefix("telephone", "te")) @@ -527,7 +527,7 @@ Example: import "core:fmt" import "core:strings" - strings_has_suffix_example :: proc() { + has_suffix_example :: proc() { fmt.println(strings.has_suffix("todo.txt", ".txt")) fmt.println(strings.has_suffix("todo.doc", ".txt")) fmt.println(strings.has_suffix("todo.doc.txt", ".txt")) @@ -559,7 +559,7 @@ Example: import "core:fmt" import "core:strings" - strings_join_example :: proc() { + join_example :: proc() { a := [?]string { "a", "b", "c" } fmt.println(strings.join(a[:], " ")) fmt.println(strings.join(a[:], "-")) @@ -644,7 +644,7 @@ Example: import "core:fmt" import "core:strings" - strings_concatenate_example :: proc() { + concatenate_example :: proc() { a := [?]string { "a", "b", "c" } fmt.println(strings.concatenate(a[:])) } @@ -716,7 +716,7 @@ Example: import "core:fmt" import "core:strings" - strings_cut_example :: proc() { + cut_example :: proc() { fmt.println(strings.cut("some example text", 0, 4)) // -> "some" fmt.println(strings.cut("some example text", 2, 2)) // -> "me" fmt.println(strings.cut("some example text", 5, 7)) // -> "example" @@ -863,7 +863,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_example :: proc() { + split_example :: proc() { s := "aaa.bbb.ccc.ddd.eee" // 5 parts ss := strings.split(s, ".") fmt.println(ss) @@ -895,7 +895,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_n_example :: proc() { + split_n_example :: proc() { s := "aaa.bbb.ccc.ddd.eee" // 5 parts present ss := strings.split_n(s, ".",3) // total of 3 wanted fmt.println(ss) @@ -927,7 +927,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_after_example :: proc() { + split_after_example :: proc() { a := "aaa.bbb.ccc.ddd.eee" // 5 parts aa := strings.split_after(a, ".") fmt.println(aa) @@ -961,7 +961,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_after_n_example :: proc() { + split_after_n_example :: proc() { a := "aaa.bbb.ccc.ddd.eee" aa := strings.split_after_n(a, ".", 3) fmt.println(aa) @@ -1032,7 +1032,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_by_byte_iterator_example :: proc() { + split_by_byte_iterator_example :: proc() { text := "a.b.c.d.e" for str in strings.split_by_byte_iterator(&text, '.') { fmt.println(str) // every loop -> a b c d e @@ -1077,7 +1077,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_iterator_example :: proc() { + split_iterator_example :: proc() { text := "a.b.c.d.e" for str in strings.split_iterator(&text, ".") { fmt.println(str) @@ -1111,7 +1111,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_after_iterator_example :: proc() { + split_after_iterator_example :: proc() { text := "a.b.c.d.e" for str in strings.split_after_iterator(&text, ".") { fmt.println(str) @@ -1167,7 +1167,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_lines_example :: proc() { + split_lines_example :: proc() { a := "a\nb\nc\nd\ne" b := strings.split_lines(a) fmt.println(b) @@ -1203,7 +1203,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_lines_n_example :: proc() { + split_lines_n_example :: proc() { a := "a\nb\nc\nd\ne" b := strings.split_lines_n(a, 3) fmt.println(b) @@ -1240,7 +1240,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_lines_after_example :: proc() { + split_lines_after_example :: proc() { a := "a\nb\nc\nd\ne" b := strings.split_lines_after(a) fmt.println(b) @@ -1279,7 +1279,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_lines_after_n_example :: proc() { + split_lines_after_n_example :: proc() { a := "a\nb\nc\nd\ne" b := strings.split_lines_after_n(a, 3) fmt.println(b) @@ -1314,7 +1314,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_lines_iterator_example :: proc() { + split_lines_iterator_example :: proc() { text := "a\nb\nc\nd\ne" for str in strings.split_lines_iterator(&text) { fmt.print(str) // every loop -> a b c d e @@ -1346,7 +1346,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_lines_after_iterator_example :: proc() { + split_lines_after_iterator_example :: proc() { text := "a\nb\nc\nd\ne\n" for str in strings.split_lines_after_iterator(&text) { fmt.print(str) // every loop -> a\n b\n c\n d\n e\n @@ -1382,7 +1382,7 @@ Example: import "core:fmt" import "core:strings" - strings_index_byte_example :: proc() { + index_byte_example :: proc() { fmt.println(strings.index_byte("test", 't')) fmt.println(strings.index_byte("test", 'e')) fmt.println(strings.index_byte("test", 'x')) @@ -1416,7 +1416,7 @@ Example: import "core:fmt" import "core:strings" - strings_last_index_byte_example :: proc() { + last_index_byte_example :: proc() { fmt.println(strings.last_index_byte("test", 't')) fmt.println(strings.last_index_byte("test", 'e')) fmt.println(strings.last_index_byte("test", 'x')) @@ -1450,7 +1450,7 @@ Example: import "core:fmt" import "core:strings" - strings_index_rune_example :: proc() { + index_rune_example :: proc() { fmt.println(strings.index_rune("abcädef", 'x')) fmt.println(strings.index_rune("abcädef", 'a')) fmt.println(strings.index_rune("abcädef", 'b')) @@ -1505,7 +1505,7 @@ Example: import "core:fmt" import "core:strings" - strings_index_example :: proc() { + index_example :: proc() { fmt.println(strings.index("test", "t")) fmt.println(strings.index("test", "te")) fmt.println(strings.index("test", "st")) @@ -1579,7 +1579,7 @@ Example: import "core:fmt" import "core:strings" - strings_last_index_example :: proc() { + last_index_example :: proc() { fmt.println(strings.last_index("test", "t")) fmt.println(strings.last_index("test", "te")) fmt.println(strings.last_index("test", "st")) @@ -1651,7 +1651,7 @@ Example: import "core:fmt" import "core:strings" - strings_index_any_example :: proc() { + index_any_example :: proc() { fmt.println(strings.index_any("test", "s")) fmt.println(strings.index_any("test", "se")) fmt.println(strings.index_any("test", "et")) @@ -1713,7 +1713,7 @@ Example: import "core:fmt" import "core:strings" - strings_last_index_any_example :: proc() { + last_index_any_example :: proc() { fmt.println(strings.last_index_any("test", "s")) fmt.println(strings.last_index_any("test", "se")) fmt.println(strings.last_index_any("test", "et")) @@ -1831,7 +1831,7 @@ Example: import "core:fmt" import "core:strings" - strings_count_example :: proc() { + count_example :: proc() { fmt.println(strings.count("abbccc", "a")) fmt.println(strings.count("abbccc", "b")) fmt.println(strings.count("abbccc", "c")) @@ -1901,7 +1901,7 @@ Example: import "core:fmt" import "core:strings" - strings_repeat_example :: proc() { + repeat_example :: proc() { fmt.println(strings.repeat("abc", 2)) } @@ -1943,7 +1943,7 @@ Example: import "core:fmt" import "core:strings" - strings_replace_all_example :: proc() { + replace_all_example :: proc() { fmt.println(strings.replace_all("xyzxyz", "xyz", "abc")) fmt.println(strings.replace_all("xyzxyz", "abc", "xyz")) fmt.println(strings.replace_all("xyzxyz", "xy", "z")) @@ -1978,7 +1978,7 @@ Example: import "core:fmt" import "core:strings" - strings_replace_example :: proc() { + replace_example :: proc() { fmt.println(strings.replace("xyzxyz", "xyz", "abc", 2)) fmt.println(strings.replace("xyzxyz", "xyz", "abc", 1)) fmt.println(strings.replace("xyzxyz", "abc", "xyz", -1)) @@ -2050,7 +2050,7 @@ Example: import "core:fmt" import "core:strings" - strings_remove_example :: proc() { + remove_example :: proc() { fmt.println(strings.remove("abcabc", "abc", 1)) fmt.println(strings.remove("abcabc", "abc", -1)) fmt.println(strings.remove("abcabc", "a", -1)) @@ -2085,7 +2085,7 @@ Example: import "core:fmt" import "core:strings" - strings_remove_all_example :: proc() { + remove_all_example :: proc() { fmt.println(strings.remove_all("abcabc", "abc")) fmt.println(strings.remove_all("abcabc", "a")) fmt.println(strings.remove_all("abcabc", "x")) @@ -2148,7 +2148,7 @@ Example: import "core:fmt" import "core:strings" - strings_index_proc_example :: proc() { + index_proc_example :: proc() { call :: proc(r: rune) -> bool { return r == 'a' } @@ -2223,7 +2223,7 @@ Example: import "core:fmt" import "core:strings" - strings_trim_left_proc_example :: proc() { + trim_left_proc_example :: proc() { find :: proc(r: rune) -> bool { return r == 'x' } @@ -2274,7 +2274,7 @@ Example: import "core:fmt" import "core:strings" - strings_trim_right_proc_example :: proc() { + trim_right_proc_example :: proc() { find :: proc(r: rune) -> bool { return r != 't' } @@ -2462,7 +2462,7 @@ Example: import "core:fmt" import "core:strings" - strings_trim_prefix_example :: proc() { + trim_prefix_example :: proc() { fmt.println(strings.trim_prefix("testing", "test")) fmt.println(strings.trim_prefix("testing", "abc")) } @@ -2493,7 +2493,7 @@ Example: import "core:fmt" import "core:strings" - strings_trim_suffix_example :: proc() { + trim_suffix_example :: proc() { fmt.println(strings.trim_suffix("todo.txt", ".txt")) fmt.println(strings.trim_suffix("todo.doc", ".txt")) } @@ -2529,7 +2529,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_multi_example :: proc() { + split_multi_example :: proc() { splits := [?]string { "---", "~~~", ".", "_", "," } res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:]) fmt.println(res) // -> [testing, this, out, nice, done, last] @@ -2594,7 +2594,7 @@ Example: import "core:fmt" import "core:strings" - strings_split_multi_iterate_example :: proc() { + split_multi_iterate_example :: proc() { it := "testing,this.out_nice---done~~~last" splits := [?]string { "---", "~~~", ".", "_", "," } for str in strings.split_multi_iterate(&it, splits[:]) { @@ -2654,7 +2654,7 @@ Example: import "core:fmt" import "core:strings" - strings_scrub_example :: proc() { + scrub_example :: proc() { text := "Hello\xC0\x80World" fmt.println(strings.scrub(text, "?")) // -> "Hello?World" } @@ -2711,7 +2711,7 @@ Example: import "core:fmt" import "core:strings" - strings_reverse_example :: proc() { + reverse_example :: proc() { a := "abcxyz" b := strings.reverse(a) fmt.println(a, b) @@ -2753,7 +2753,7 @@ Example: import "core:fmt" import "core:strings" - strings_expand_tabs_example :: proc() { + expand_tabs_example :: proc() { text := "abc1\tabc2\tabc3" fmt.println(strings.expand_tabs(text, 4)) } @@ -2820,7 +2820,7 @@ Example: import "core:fmt" import "core:strings" - strings_partition_example :: proc() { + partition_example :: proc() { text := "testing this out" head, match, tail := strings.partition(text, " this ") // -> head: "testing", match: " this ", tail: "out" fmt.println(head, match, tail) diff --git a/tests/documentation/documentation_tester.odin b/tests/documentation/documentation_tester.odin index 5d2b07b28..09c565a51 100644 --- a/tests/documentation/documentation_tester.odin +++ b/tests/documentation/documentation_tester.odin @@ -1,6 +1,7 @@ package documentation_tester import "core:os" +import "core:io" import "core:fmt" import "core:strings" import "core:odin/ast" @@ -9,7 +10,8 @@ import "core:c/libc" import doc "core:odin/doc-format" Example_Test :: struct { - name: string, + entity_name: string, + package_name: string, example_code: []string, expected_output: []string, } @@ -59,7 +61,7 @@ main :: proc() { if len(os.args) != 2 { errorf("expected path to odin executable") } - g_path_to_odin = os.args[1] + g_path_to_odin = os.args[1] data, ok := os.read_entire_file("all.odin-doc") if !ok { errorf("unable to read file: all.odin-doc") @@ -80,37 +82,41 @@ main :: proc() { pkgs := array(g_header.pkgs) entities := array(g_header.entities) - path_prefix: string - { - fullpaths: [dynamic]string - defer delete(fullpaths) + path_prefix: string + { + fullpaths: [dynamic]string + defer delete(fullpaths) - for pkg in pkgs[1:] { - append(&fullpaths, str(pkg.fullpath)) - } - path_prefix = common_prefix(fullpaths[:]) - } + for pkg in pkgs[1:] { + append(&fullpaths, str(pkg.fullpath)) + } + path_prefix = common_prefix(fullpaths[:]) + } - for pkg in pkgs[1:] { - entries_array := array(pkg.entries) - fullpath := str(pkg.fullpath) - path := strings.trim_prefix(fullpath, path_prefix) - if ! strings.has_prefix(path, "core/") { - continue - } - trimmed_path := strings.trim_prefix(path, "core/") - if strings.has_prefix(trimmed_path, "sys") { - continue - } - if strings.contains(trimmed_path, "/_") { - continue - } - for entry in entries_array { - entity := entities[entry.entity] - find_and_add_examples(str(entity.docs), fmt.aprintf("%v.%v", str(pkg.name), str(entity.name))) - } - } - write_test_suite(g_examples_to_verify[:]) + for pkg in pkgs[1:] { + entries_array := array(pkg.entries) + fullpath := str(pkg.fullpath) + path := strings.trim_prefix(fullpath, path_prefix) + if ! strings.has_prefix(path, "core/") { + continue + } + trimmed_path := strings.trim_prefix(path, "core/") + if strings.has_prefix(trimmed_path, "sys") { + continue + } + if strings.contains(trimmed_path, "/_") { + continue + } + for entry in entries_array { + entity := entities[entry.entity] + find_and_add_examples( + docs = str(entity.docs), + package_name = str(pkg.name), + entity_name = str(entity.name), + ) + } + } + write_test_suite(g_examples_to_verify[:]) if g_bad_doc { errorf("We created bad documentation!") } @@ -118,11 +124,11 @@ main :: proc() { if ! run_test_suite() { errorf("Test suite failed!") } - fmt.println("Examples verified") + fmt.println("Examples verified") } // NOTE: this is a pretty close copy paste from the website pkg documentation on parsing the docs -find_and_add_examples :: proc(docs: string, name: string = "") { +find_and_add_examples :: proc(docs: string, package_name: string, entity_name: string) { if docs == "" { return } @@ -186,31 +192,31 @@ find_and_add_examples :: proc(docs: string, name: string = "") { } if i-start > 0 && (curr_block_kind != next_block_kind) { - insert_block(Block{curr_block_kind, lines[start:i]}, &example_block, &output_block, name) + insert_block(Block{curr_block_kind, lines[start:i]}, &example_block, &output_block, entity_name) curr_block_kind, start = next_block_kind, i } } if start < len(lines) { - insert_block(Block{curr_block_kind, lines[start:]}, &example_block, &output_block, name) + insert_block(Block{curr_block_kind, lines[start:]}, &example_block, &output_block, entity_name) } if output_block.kind == .Output && example_block.kind != .Example { - fmt.eprintf("The documentation for %q has an output block but no example\n", name) + fmt.eprintf("The documentation for %q has an output block but no example\n", entity_name) g_bad_doc = true } // Write example and output block if they're both present if example_block.kind == .Example && output_block.kind == .Output { - { - // Example block starts with - // `Example:` and a number of white spaces, - lines := &example_block.lines - for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Example:")) { - lines^ = lines[1:] - } - } - { + { + // Example block starts with + // `Example:` and a number of white spaces, + lines := &example_block.lines + for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Example:")) { + lines^ = lines[1:] + } + } + { // Output block starts with // `Output:` and a number of white spaces, lines := &output_block.lines @@ -221,21 +227,26 @@ find_and_add_examples :: proc(docs: string, name: string = "") { for len(lines) > 0 && (strings.trim_space(lines[len(lines) - 1]) == "") { lines^ = lines[:len(lines) - 1] } - } - // Remove first layer of tabs which are always present - for line in &example_block.lines { - line = strings.trim_prefix(line, "\t") - } - for line in &output_block.lines { - line = strings.trim_prefix(line, "\t") - } - append(&g_examples_to_verify, Example_Test { name = name, example_code = example_block.lines, expected_output = output_block.lines }) + } + // Remove first layer of tabs which are always present + for line in &example_block.lines { + line = strings.trim_prefix(line, "\t") + } + for line in &output_block.lines { + line = strings.trim_prefix(line, "\t") + } + append(&g_examples_to_verify, Example_Test { + entity_name = entity_name, + package_name = package_name, + example_code = example_block.lines, + expected_output = output_block.lines, + }) } } write_test_suite :: proc(example_tests: []Example_Test) { - TEST_SUITE_DIRECTORY :: "verify" + TEST_SUITE_DIRECTORY :: "verify" os.remove_directory(TEST_SUITE_DIRECTORY) os.make_directory(TEST_SUITE_DIRECTORY) @@ -325,7 +336,6 @@ main :: proc() { } code_string := strings.to_string(example_build) - code_test_name: string example_ast := ast.File { src = code_string } odin_parser := parser.default_parser() @@ -335,11 +345,15 @@ main :: proc() { continue } if odin_parser.error_count > 0 { - fmt.eprintf("Errors on the following code generated for %q:\n%v\n", test.name, code_string) + fmt.eprintf("Errors on the following code generated for %q:\n%v\n", test.entity_name, code_string) g_bad_doc = true continue } + enforced_name := fmt.tprintf("%v_example", test.entity_name) + index_of_proc_name: int + code_test_name: string + for d in example_ast.decls { value_decl, is_value := d.derived.(^ast.Value_Decl); if ! is_value { continue @@ -353,34 +367,48 @@ main :: proc() { if len(proc_lit.type.params.list) > 0 { continue } - code_test_name = code_string[value_decl.names[0].pos.offset:value_decl.names[0].end.offset] + this_procedure_name := code_string[value_decl.names[0].pos.offset:value_decl.names[0].end.offset] + if this_procedure_name != enforced_name { + continue + } + index_of_proc_name = value_decl.names[0].pos.offset + code_test_name = this_procedure_name break } if code_test_name == "" { - fmt.eprintf("We could not any find procedure literals with no arguments in the example for %q\n", test.name) + fmt.eprintf("We could not any find procedure literals with no arguments with the identifier %q for the example for %q\n", enforced_name, test.entity_name) g_bad_doc = true continue } - strings.write_string(&test_runner, "\t") - strings.write_string(&test_runner, code_test_name) - strings.write_string(&test_runner, "()\n") + fmt.sbprintf(&test_runner, "\t%v_%v()\n", test.package_name, code_test_name) fmt.sbprintf(&test_runner, "\t_check(%q, `", code_test_name) for line in test.expected_output { strings.write_string(&test_runner, line) strings.write_string(&test_runner, "\n") } strings.write_string(&test_runner, "`)\n") - save_path := fmt.tprintf("verify/test_%s.odin", code_test_name) - if ! os.write_entire_file(save_path, transmute([]byte)code_string) { - fmt.eprintf("We could not save the file to the path %q\n", save_path) + save_path := fmt.tprintf("verify/test_%v_%v.odin", test.package_name, code_test_name) + + test_file_handle, err := os.open(save_path, os.O_WRONLY | os.O_CREATE); if err != 0 { + fmt.eprintf("We could not open the file to the path %q for writing\n", save_path) g_bad_doc = true + continue } + defer os.close(test_file_handle) + stream := os.stream_from_handle(test_file_handle) + writer, ok := io.to_writer(stream); if ! ok { + fmt.eprintf("We could not make the writer for the path %q\n", save_path) + g_bad_doc = true + continue + } + fmt.wprintf(writer, "%v%v_%v", code_string[:index_of_proc_name], test.package_name, code_string[index_of_proc_name:]) } strings.write_string(&test_runner, -` if _bad_test_found { +` + if _bad_test_found { fmt.eprintln("One or more tests failed") os.exit(1) } @@ -389,6 +417,5 @@ main :: proc() { } run_test_suite :: proc() -> bool { - cmd := fmt.tprintf("%v run verify", g_path_to_odin) - return libc.system(strings.clone_to_cstring(cmd)) == 0 + return libc.system(fmt.caprintf("%v run verify", g_path_to_odin)) == 0 } \ No newline at end of file