diff --git a/test/helpers/random_helpers.cc b/test/helpers/random_helpers.cc index d79475c7..b3de98e2 100644 --- a/test/helpers/random_helpers.cc +++ b/test/helpers/random_helpers.cc @@ -1,3 +1,4 @@ +#include "helpers/random_helpers.h" #include #include #include @@ -6,54 +7,50 @@ using std::string; using std::vector; -static std::default_random_engine engine; +Generator default_generator(0); unsigned get_time_as_seed() { return time(nullptr); } -void random_reseed(unsigned seed) { +void Generator::reseed(unsigned seed) { engine.seed(seed); } -unsigned random_unsigned() { - return std::uniform_int_distribution()(engine); +unsigned Generator::operator()() { + return distribution(engine); } -unsigned random_unsigned(unsigned max) { - return std::uniform_int_distribution(0, max - 1)(engine); +unsigned Generator::operator()(unsigned max) { + return distribution(engine) % max; } -static string random_string(char min, char max) { +string Generator::str(char min, char max) { string result; - size_t length = random_unsigned(12); + size_t length = operator()(12); for (size_t i = 0; i < length; i++) { - result += (min + random_unsigned(max - min)); + result += (min + operator()(max - min)); } return result; } -static string random_char(string characters) { - size_t index = random_unsigned(characters.size()); - return string() + characters[index]; -} - -string random_words(size_t count) { +string Generator::words(size_t count) { string result; bool just_inserted_word = false; for (size_t i = 0; i < count; i++) { - if (random_unsigned(10) < 6) { - result += random_char("!(){}[]<>+-="); + if (operator()(10) < 6) { + const char *operator_characters = "!(){}[]<>+-="; + result += operator_characters[operator()(strlen(operator_characters))]; } else { if (just_inserted_word) result += " "; - result += random_string('a', 'z'); + result += str('a', 'z'); just_inserted_word = true; } } return result; } -string select_random(const vector &list) { - return list[random_unsigned(list.size())]; +string Generator::select(const vector &list) { + return list[operator()(list.size())]; } diff --git a/test/helpers/random_helpers.h b/test/helpers/random_helpers.h index b66c4aee..d415aafe 100644 --- a/test/helpers/random_helpers.h +++ b/test/helpers/random_helpers.h @@ -3,12 +3,25 @@ #include #include +#include unsigned get_time_as_seed(); -void random_reseed(unsigned); -unsigned random_unsigned(); -unsigned random_unsigned(unsigned max); -std::string random_words(size_t count); -std::string select_random(const std::vector &); + +class Generator { + std::default_random_engine engine; + std::uniform_int_distribution distribution; + +public: + Generator(uint32_t seed) : engine{seed} {} + + void reseed(unsigned); + unsigned operator()(); + unsigned operator()(unsigned max); + std::string words(size_t count); + std::string str(char min, char max); + std::string select(const std::vector &); +}; + +extern Generator default_generator; #endif // HELPERS_RANDOM_HELPERS_H_ diff --git a/test/integration/real_grammars.cc b/test/integration/real_grammars.cc index a628177f..02752a34 100644 --- a/test/integration/real_grammars.cc +++ b/test/integration/real_grammars.cc @@ -76,9 +76,9 @@ for (auto &language_name : test_languages) { set> insertions; for (size_t i = 0; i < 60; i++) { - size_t edit_position = random_unsigned(utf8_char_count(entry.input)); - size_t deletion_size = random_unsigned(utf8_char_count(entry.input) - edit_position); - string inserted_text = random_words(random_unsigned(4) + 1); + size_t edit_position = default_generator(utf8_char_count(entry.input)); + size_t deletion_size = default_generator(utf8_char_count(entry.input) - edit_position); + string inserted_text = default_generator.words(default_generator(4) + 1); if (insertions.insert({edit_position, inserted_text}).second) { it(("parses " + entry.description + diff --git a/test/runtime/tree_test.cc b/test/runtime/tree_test.cc index a87c754b..6d038220 100644 --- a/test/runtime/tree_test.cc +++ b/test/runtime/tree_test.cc @@ -1,4 +1,5 @@ #include "test_helper.h" +#include #include "runtime/alloc.h" #include "helpers/record_alloc.h" #include "helpers/stream_methods.h" @@ -8,6 +9,11 @@ #include "helpers/stderr_logger.h" #include "helpers/spy_input.h" #include "helpers/load_language.h" +#include "helpers/random_helpers.h" +#include "helpers/read_test_entries.h" +#include "helpers/encoding_helpers.h" +#include "helpers/tree_helpers.h" +#include TSPoint point(uint32_t row, uint32_t column) { TSPoint result = {row, column}; @@ -37,6 +43,57 @@ describe("Tree", [&]() { AssertThat(actual, Equals(expected)); }; + describe("copy()", [&]() { + it("returns a tree that can be safely used while the current tree is edited", [&]() { + const TSLanguage *language = load_real_language("javascript"); + ts_parser_set_language(parser, language); + string source_code = examples_for_language("javascript")[0].input; + + input = new SpyInput(source_code, 32); + TSTree *original_tree = ts_parser_parse(parser, nullptr, input->input()); + + vector> new_trees; + for (unsigned i = 0; i < 8; i++) { + TSTree *tree_copy = ts_tree_copy(original_tree); + new_trees.push_back(std::async([i, tree_copy, &source_code, language]() { + Generator random(TREE_SITTER_SEED + i); + + TSTree *tree = tree_copy; + TSParser *parser = ts_parser_new(); + ts_parser_set_language(parser, language); + SpyInput *input = new SpyInput(source_code, 32); + + for (unsigned j = 0; j < 30; j++) { + usleep(random(200)); + + size_t edit_position = random(utf8_char_count(input->content)); + size_t deletion_size = random(utf8_char_count(input->content) - edit_position); + string inserted_text = random.words(random(4) + 1); + + TSInputEdit edit = input->replace(edit_position, deletion_size, inserted_text); + ts_tree_edit(tree, &edit); + + TSTree *new_tree = ts_parser_parse(parser, tree, input->input()); + ts_tree_delete(tree); + tree = new_tree; + } + + ts_parser_delete(parser); + delete input; + + return tree; + })); + } + + for (auto &future : new_trees) { + future.wait(); + TSTree *new_tree = future.get(); + assert_consistent_tree_sizes(ts_tree_root_node(new_tree)); + ts_tree_delete(new_tree); + } + }); + }); + describe("get_changed_ranges()", [&]() { before_each([&]() { ts_parser_set_language(parser, load_real_language("javascript")); diff --git a/test/tests.cc b/test/tests.cc index 303f9059..0d8c23e7 100644 --- a/test/tests.cc +++ b/test/tests.cc @@ -12,7 +12,7 @@ int main(int argc, char *argv[]) { } printf("Random seed: %d\n", TREE_SITTER_SEED); - random_reseed(TREE_SITTER_SEED); + default_generator.reseed(TREE_SITTER_SEED); return bandit::run(argc, argv); }