Chromium Code Reviews| Index: test/cctest/test-strings.cc |
| diff --git a/test/cctest/test-strings.cc b/test/cctest/test-strings.cc |
| index 26b73392cc92415115d70f1351d3d2b41030b336..4fb32d5b861ade3bebd27cf5f5ac4111d4a67887 100644 |
| --- a/test/cctest/test-strings.cc |
| +++ b/test/cctest/test-strings.cc |
| @@ -85,7 +85,6 @@ static void InitializeVM() { |
| } |
| -static const int NUMBER_OF_BUILDING_BLOCKS = 256; |
| static const int DEEP_DEPTH = 8 * 1024; |
| static const int SUPER_DEEP_DEPTH = 80 * 1024; |
| @@ -191,7 +190,7 @@ static void InitializeBuildingBlocks(Handle<String>* building_blocks, |
| case 3: { |
| char* buf = zone->NewArray<char>(len); |
| for (int j = 0; j < len; j++) { |
| - buf[j] = rng->next(128); |
| + buf[j] = rng->next(0x80); |
| } |
| AsciiResource* resource = |
| new(zone) AsciiResource(Vector<const char>(buf, len)); |
| @@ -213,155 +212,6 @@ static void InitializeBuildingBlocks(Handle<String>* building_blocks, |
| } |
| -static Handle<String> ConstructLeft( |
| - Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS], |
| - int depth) { |
| - Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); |
| - for (int i = 0; i < depth; i++) { |
| - answer = FACTORY->NewConsString( |
| - answer, |
| - building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]); |
| - } |
| - return answer; |
| -} |
| - |
| - |
| -static Handle<String> ConstructRight( |
| - Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS], |
| - int depth) { |
| - Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); |
| - for (int i = depth - 1; i >= 0; i--) { |
| - answer = FACTORY->NewConsString( |
| - building_blocks[i % NUMBER_OF_BUILDING_BLOCKS], |
| - answer); |
| - } |
| - return answer; |
| -} |
| - |
| - |
| -static Handle<String> ConstructBalancedHelper( |
| - Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS], |
| - int from, |
| - int to) { |
| - CHECK(to > from); |
| - if (to - from == 1) { |
| - return building_blocks[from % NUMBER_OF_BUILDING_BLOCKS]; |
| - } |
| - if (to - from == 2) { |
| - return FACTORY->NewConsString( |
| - building_blocks[from % NUMBER_OF_BUILDING_BLOCKS], |
| - building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS]); |
| - } |
| - Handle<String> part1 = |
| - ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2)); |
| - Handle<String> part2 = |
| - ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to); |
| - return FACTORY->NewConsString(part1, part2); |
| -} |
| - |
| - |
| -static Handle<String> ConstructBalanced( |
| - Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) { |
| - return ConstructBalancedHelper(building_blocks, 0, DEEP_DEPTH); |
| -} |
| - |
| - |
| -static StringInputBuffer buffer; |
| -static ConsStringIteratorOp cons_string_iterator_op_1; |
| -static ConsStringIteratorOp cons_string_iterator_op_2; |
| - |
| -static void Traverse(Handle<String> s1, Handle<String> s2) { |
| - int i = 0; |
| - buffer.Reset(*s1); |
| - StringCharacterStream character_stream_1(*s1, 0, &cons_string_iterator_op_1); |
| - StringCharacterStream character_stream_2(*s2, 0, &cons_string_iterator_op_2); |
| - StringInputBuffer buffer2(*s2); |
| - while (buffer.has_more()) { |
| - CHECK(buffer2.has_more()); |
| - CHECK(character_stream_1.HasMore()); |
| - CHECK(character_stream_2.HasMore()); |
| - uint16_t c = buffer.GetNext(); |
| - CHECK_EQ(c, buffer2.GetNext()); |
| - CHECK_EQ(c, character_stream_1.GetNext()); |
| - CHECK_EQ(c, character_stream_2.GetNext()); |
| - i++; |
| - } |
| - CHECK(!character_stream_1.HasMore()); |
| - CHECK(!character_stream_2.HasMore()); |
| - CHECK_EQ(s1->length(), i); |
| - CHECK_EQ(s2->length(), i); |
| -} |
| - |
| - |
| -static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) { |
| - int i = 0; |
| - buffer.Reset(*s1); |
| - StringInputBuffer buffer2(*s2); |
| - StringCharacterStream character_stream_1(*s1, 0, &cons_string_iterator_op_1); |
| - StringCharacterStream character_stream_2(*s2, 0, &cons_string_iterator_op_2); |
| - while (buffer.has_more() && i < chars) { |
| - CHECK(buffer2.has_more()); |
| - CHECK(character_stream_1.HasMore()); |
| - CHECK(character_stream_2.HasMore()); |
| - uint16_t c = buffer.GetNext(); |
| - CHECK_EQ(c, buffer2.GetNext()); |
| - CHECK_EQ(c, character_stream_1.GetNext()); |
| - CHECK_EQ(c, character_stream_2.GetNext()); |
| - i++; |
| - } |
| - s1->Get(s1->length() - 1); |
| - s2->Get(s2->length() - 1); |
| -} |
| - |
| - |
| -TEST(Traverse) { |
| - printf("TestTraverse\n"); |
| - InitializeVM(); |
| - v8::HandleScope scope; |
| - Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]; |
| - ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); |
| - RandomNumberGenerator rng; |
| - rng.init(); |
| - InitializeBuildingBlocks( |
| - building_blocks, NUMBER_OF_BUILDING_BLOCKS, false, &rng); |
| - Handle<String> flat = ConstructBalanced(building_blocks); |
| - FlattenString(flat); |
| - Handle<String> left_asymmetric = ConstructLeft(building_blocks, DEEP_DEPTH); |
| - Handle<String> right_asymmetric = ConstructRight(building_blocks, DEEP_DEPTH); |
| - Handle<String> symmetric = ConstructBalanced(building_blocks); |
| - printf("1\n"); |
| - Traverse(flat, symmetric); |
| - printf("2\n"); |
| - Traverse(flat, left_asymmetric); |
| - printf("3\n"); |
| - Traverse(flat, right_asymmetric); |
| - printf("4\n"); |
| - Handle<String> left_deep_asymmetric = |
| - ConstructLeft(building_blocks, SUPER_DEEP_DEPTH); |
| - Handle<String> right_deep_asymmetric = |
| - ConstructRight(building_blocks, SUPER_DEEP_DEPTH); |
| - printf("5\n"); |
| - TraverseFirst(left_asymmetric, left_deep_asymmetric, 1050); |
| - printf("6\n"); |
| - TraverseFirst(left_asymmetric, right_deep_asymmetric, 65536); |
| - printf("7\n"); |
| - FlattenString(left_asymmetric); |
| - printf("10\n"); |
| - Traverse(flat, left_asymmetric); |
| - printf("11\n"); |
| - FlattenString(right_asymmetric); |
| - printf("12\n"); |
| - Traverse(flat, right_asymmetric); |
| - printf("14\n"); |
| - FlattenString(symmetric); |
| - printf("15\n"); |
| - Traverse(flat, symmetric); |
| - printf("16\n"); |
| - FlattenString(left_deep_asymmetric); |
| - printf("18\n"); |
| -} |
| - |
| - |
| class ConsStringStats { |
| public: |
| ConsStringStats() { |
| @@ -399,8 +249,11 @@ void ConsStringStats::VerifyEqual(const ConsStringStats& that) const { |
| class ConsStringGenerationData { |
| public: |
| - ConsStringGenerationData(); |
| + static const int kNumberOfBuildingBlocks = 256; |
| + explicit ConsStringGenerationData(bool long_blocks); |
| void Reset(); |
| + inline Handle<String> block(int offset); |
| + inline Handle<String> block(uint32_t offset); |
| // Input variables. |
| double early_termination_threshold_; |
| double leftness_; |
| @@ -408,7 +261,7 @@ class ConsStringGenerationData { |
| double empty_leaf_threshold_; |
| unsigned max_leaves_; |
| // Cached data. |
| - Handle<String> building_blocks_[NUMBER_OF_BUILDING_BLOCKS]; |
| + Handle<String> building_blocks_[kNumberOfBuildingBlocks]; |
| String* empty_string_; |
| RandomNumberGenerator rng_; |
| // Stats. |
| @@ -419,15 +272,26 @@ class ConsStringGenerationData { |
| }; |
| -ConsStringGenerationData::ConsStringGenerationData() { |
| +ConsStringGenerationData::ConsStringGenerationData(bool long_blocks) { |
| rng_.init(); |
| InitializeBuildingBlocks( |
| - building_blocks_, NUMBER_OF_BUILDING_BLOCKS, true, &rng_); |
| + building_blocks_, kNumberOfBuildingBlocks, long_blocks, &rng_); |
| empty_string_ = Isolate::Current()->heap()->empty_string(); |
| Reset(); |
| } |
| +Handle<String> ConsStringGenerationData::block(uint32_t offset) { |
| + return building_blocks_[offset % kNumberOfBuildingBlocks ]; |
| +} |
| + |
| + |
| +Handle<String> ConsStringGenerationData::block(int offset) { |
| + CHECK_GE(offset, 0); |
| + return building_blocks_[offset % kNumberOfBuildingBlocks]; |
| +} |
| + |
| + |
| void ConsStringGenerationData::Reset() { |
| early_termination_threshold_ = 0.01; |
| leftness_ = 0.75; |
| @@ -436,17 +300,19 @@ void ConsStringGenerationData::Reset() { |
| max_leaves_ = 1000; |
| stats_.Reset(); |
| early_terminations_ = 0; |
| + rng_.init(); |
| } |
| -void VerifyConsString(ConsString* cons_string, ConsStringStats* stats) { |
| +void AccumulateStats(ConsString* cons_string, ConsStringStats* stats) { |
| int left_length = cons_string->first()->length(); |
| int right_length = cons_string->second()->length(); |
| CHECK(cons_string->length() == left_length + right_length); |
| // Check left side. |
| - if (cons_string->first()->IsConsString()) { |
| + bool left_is_cons = cons_string->first()->IsConsString(); |
| + if (left_is_cons) { |
| stats->left_traversals_++; |
| - VerifyConsString(ConsString::cast(cons_string->first()), stats); |
| + AccumulateStats(ConsString::cast(cons_string->first()), stats); |
| } else { |
| CHECK_NE(left_length, 0); |
| stats->leaves_++; |
| @@ -455,16 +321,29 @@ void VerifyConsString(ConsString* cons_string, ConsStringStats* stats) { |
| // Check right side. |
| if (cons_string->second()->IsConsString()) { |
| stats->right_traversals_++; |
| - VerifyConsString(ConsString::cast(cons_string->second()), stats); |
| + AccumulateStats(ConsString::cast(cons_string->second()), stats); |
| } else { |
| - if (right_length == 0) stats->empty_leaves_++; |
| + if (right_length == 0) { |
| + stats->empty_leaves_++; |
| + CHECK(!left_is_cons); |
| + } |
| stats->leaves_++; |
| stats->chars_ += right_length; |
| } |
| } |
| -void VerifyConsStringWithOperator( |
| +void AccumulateStats(Handle<String> cons_string, ConsStringStats* stats) { |
| + AssertNoAllocation no_alloc; |
| + if (cons_string->IsConsString()) { |
| + return AccumulateStats(ConsString::cast(*cons_string), stats); |
| + } |
| + // This string got flattened by gc. |
| + stats->chars_ += cons_string->length(); |
| +} |
| + |
| + |
| +void AccumulateStatsWithOperator( |
| ConsString* cons_string, ConsStringStats* stats) { |
| // Init op. |
| ConsStringIteratorOp op; |
| @@ -506,11 +385,11 @@ void VerifyConsString(Handle<String> root, ConsStringGenerationData* data) { |
| CHECK((unsigned)root->length() == data->stats_.chars_); |
| // Recursive verify. |
| ConsStringStats stats; |
| - VerifyConsString(ConsString::cast(*root), &stats); |
| + AccumulateStats(ConsString::cast(*root), &stats); |
| stats.VerifyEqual(data->stats_); |
| // Iteratively verify. |
| stats.Reset(); |
| - VerifyConsStringWithOperator(ConsString::cast(*root), &stats); |
| + AccumulateStatsWithOperator(ConsString::cast(*root), &stats); |
| // Don't see these. Must copy over. |
| stats.empty_leaves_ = data->stats_.empty_leaves_; |
| stats.left_traversals_ = data->stats_.left_traversals_; |
| @@ -542,23 +421,32 @@ static Handle<String> ConstructRandomString(ConsStringGenerationData* data, |
| // Generate left string. |
| Handle<String> left; |
| if (terminate_left) { |
| - left = data->building_blocks_[data->rng_.next(NUMBER_OF_BUILDING_BLOCKS)]; |
| + left = data->block(data->rng_.next()); |
| data->stats_.leaves_++; |
| data->stats_.chars_ += left->length(); |
| } else { |
| - left = ConstructRandomString(data, max_recursion - 1); |
| data->stats_.left_traversals_++; |
| } |
| // Generate right string. |
| Handle<String> right; |
| if (terminate_right) { |
| - right = data->building_blocks_[data->rng_.next(NUMBER_OF_BUILDING_BLOCKS)]; |
| + right = data->block(data->rng_.next()); |
| data->stats_.leaves_++; |
| data->stats_.chars_ += right->length(); |
| } else { |
| - right = ConstructRandomString(data, max_recursion - 1); |
| data->stats_.right_traversals_++; |
| } |
| + // Generate the necessary sub-nodes recursively. |
| + if (!terminate_right) { |
| + // Need to balance generation fairly. |
| + if (!terminate_left && data->rng_.next(0.5)) { |
| + left = ConstructRandomString(data, max_recursion - 1); |
| + } |
| + right = ConstructRandomString(data, max_recursion - 1); |
| + } |
| + if (!terminate_left && left.is_null()) { |
| + left = ConstructRandomString(data, max_recursion - 1); |
| + } |
| // Build the cons string. |
| Handle<String> root = FACTORY->NewConsString(left, right); |
| CHECK(root->IsConsString() && !root->IsFlat()); |
| @@ -572,63 +460,169 @@ static Handle<String> ConstructRandomString(ConsStringGenerationData* data, |
| } |
| -static const int kCharacterStreamRandomCases = 150; |
| -static const int kCharacterStreamEdgeCases = |
| - kCharacterStreamRandomCases + 5; |
| +static Handle<String> ConstructLeft( |
| + ConsStringGenerationData* data, |
| + int depth) { |
| + Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); |
| + data->stats_.leaves_++; |
| + for (int i = 0; i < depth; i++) { |
| + Handle<String> block = data->block(i); |
| + Handle<String> next = FACTORY->NewConsString(answer, block); |
| + if (next->IsConsString()) data->stats_.leaves_++; |
| + data->stats_.chars_ += block->length(); |
| + answer = next; |
| + } |
| + data->stats_.left_traversals_ = data->stats_.leaves_ - 2; |
| + return answer; |
| +} |
| -static Handle<String> BuildConsStrings(int testCase, |
| - ConsStringGenerationData* data) { |
| - // For random constructions, need to reset the generator. |
| - data->rng_.init(); |
| - for (int j = 0; j < testCase * 50; j++) { |
| - data->rng_.next(); |
| +static Handle<String> ConstructRight( |
| + ConsStringGenerationData* data, |
| + int depth) { |
| + Handle<String> answer = FACTORY->NewStringFromAscii(CStrVector("")); |
| + data->stats_.leaves_++; |
| + for (int i = depth - 1; i >= 0; i--) { |
| + Handle<String> block = data->block(i); |
| + Handle<String> next = FACTORY->NewConsString(block, answer); |
| + if (next->IsConsString()) data->stats_.leaves_++; |
| + data->stats_.chars_ += block->length(); |
| + answer = next; |
| } |
| - Handle<String> string; |
| - switch (testCase) { |
| - case 0: |
| - return ConstructBalanced(data->building_blocks_); |
| - case 1: |
| - return ConstructLeft(data->building_blocks_, DEEP_DEPTH); |
| - case 2: |
| - return ConstructRight(data->building_blocks_, DEEP_DEPTH); |
| - case 3: |
| - return ConstructLeft(data->building_blocks_, 10); |
| - case 4: |
| - return ConstructRight(data->building_blocks_, 10); |
| - case 5: |
| - return FACTORY->NewConsString( |
| - data->building_blocks_[0], data->building_blocks_[1]); |
| - default: |
| - if (testCase >= kCharacterStreamEdgeCases) { |
| - CHECK(false); |
| - return string; |
| - } |
| - // Random test case. |
| - data->Reset(); |
| - string = ConstructRandomString(data, 200); |
| - AssertNoAllocation no_alloc; |
| - VerifyConsString(string, data); |
| -#ifdef DEBUG |
| - printf( |
| - "%s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d]\n", |
| - "leaves", data->stats_.leaves_, |
| - "empty", data->stats_.empty_leaves_, |
| - "chars", data->stats_.chars_, |
| - "lefts", data->stats_.left_traversals_, |
| - "rights", data->stats_.right_traversals_, |
| - "early_terminations", data->early_terminations_); |
| -#endif |
| - return string; |
| - } |
| + data->stats_.right_traversals_ = data->stats_.leaves_ - 2; |
| + return answer; |
| +} |
| + |
| + |
| +static Handle<String> ConstructBalancedHelper( |
| + ConsStringGenerationData* data, |
| + int from, |
| + int to) { |
| + CHECK(to > from); |
| + if (to - from == 1) { |
| + data->stats_.chars_ += data->block(from)->length(); |
| + return data->block(from); |
| + } |
| + if (to - from == 2) { |
| + data->stats_.chars_ += data->block(from)->length(); |
| + data->stats_.chars_ += data->block(from+1)->length(); |
| + return FACTORY->NewConsString(data->block(from), data->block(from+1)); |
| + } |
| + Handle<String> part1 = |
| + ConstructBalancedHelper(data, from, from + ((to - from) / 2)); |
| + Handle<String> part2 = |
| + ConstructBalancedHelper(data, from + ((to - from) / 2), to); |
| + if (part1->IsConsString()) data->stats_.left_traversals_++; |
| + if (part2->IsConsString()) data->stats_.right_traversals_++; |
| + return FACTORY->NewConsString(part1, part2); |
| +} |
| + |
| + |
| +static Handle<String> ConstructBalanced( |
| + ConsStringGenerationData* data, int depth = DEEP_DEPTH) { |
| + Handle<String> string = ConstructBalancedHelper(data, 0, depth); |
| + data->stats_.leaves_ = |
| + data->stats_.left_traversals_ + data->stats_.right_traversals_ + 2; |
| + return string; |
| +} |
| + |
| + |
| +static StringInputBuffer buffer; |
| +static ConsStringIteratorOp cons_string_iterator_op_1; |
| +static ConsStringIteratorOp cons_string_iterator_op_2; |
| + |
| +static void Traverse(Handle<String> s1, Handle<String> s2) { |
| + int i = 0; |
| + buffer.Reset(*s1); |
| + StringCharacterStream character_stream_1(*s1, 0, &cons_string_iterator_op_1); |
| + StringCharacterStream character_stream_2(*s2, 0, &cons_string_iterator_op_2); |
| + StringInputBuffer buffer2(*s2); |
| + while (buffer.has_more()) { |
| + CHECK(buffer2.has_more()); |
| + CHECK(character_stream_1.HasMore()); |
| + CHECK(character_stream_2.HasMore()); |
| + uint16_t c = buffer.GetNext(); |
| + CHECK_EQ(c, buffer2.GetNext()); |
| + CHECK_EQ(c, character_stream_1.GetNext()); |
| + CHECK_EQ(c, character_stream_2.GetNext()); |
| + i++; |
| + } |
| + CHECK(!character_stream_1.HasMore()); |
| + CHECK(!character_stream_2.HasMore()); |
| + CHECK_EQ(s1->length(), i); |
| + CHECK_EQ(s2->length(), i); |
| +} |
| + |
| + |
| +static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) { |
| + int i = 0; |
| + buffer.Reset(*s1); |
| + StringInputBuffer buffer2(*s2); |
| + StringCharacterStream character_stream_1(*s1, 0, &cons_string_iterator_op_1); |
| + StringCharacterStream character_stream_2(*s2, 0, &cons_string_iterator_op_2); |
| + while (buffer.has_more() && i < chars) { |
| + CHECK(buffer2.has_more()); |
| + CHECK(character_stream_1.HasMore()); |
| + CHECK(character_stream_2.HasMore()); |
| + uint16_t c = buffer.GetNext(); |
| + CHECK_EQ(c, buffer2.GetNext()); |
| + CHECK_EQ(c, character_stream_1.GetNext()); |
| + CHECK_EQ(c, character_stream_2.GetNext()); |
| + i++; |
| + } |
| + s1->Get(s1->length() - 1); |
| + s2->Get(s2->length() - 1); |
| +} |
| + |
| + |
| +TEST(Traverse) { |
| + printf("TestTraverse\n"); |
| + InitializeVM(); |
| + v8::HandleScope scope; |
| + ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); |
| + ConsStringGenerationData data(false); |
| + Handle<String> flat = ConstructBalanced(&data); |
| + FlattenString(flat); |
| + Handle<String> left_asymmetric = ConstructLeft(&data, DEEP_DEPTH); |
| + Handle<String> right_asymmetric = ConstructRight(&data, DEEP_DEPTH); |
| + Handle<String> symmetric = ConstructBalanced(&data); |
| + printf("1\n"); |
| + Traverse(flat, symmetric); |
| + printf("2\n"); |
| + Traverse(flat, left_asymmetric); |
| + printf("3\n"); |
| + Traverse(flat, right_asymmetric); |
| + printf("4\n"); |
| + Handle<String> left_deep_asymmetric = |
| + ConstructLeft(&data, SUPER_DEEP_DEPTH); |
| + Handle<String> right_deep_asymmetric = |
| + ConstructRight(&data, SUPER_DEEP_DEPTH); |
| + printf("5\n"); |
| + TraverseFirst(left_asymmetric, left_deep_asymmetric, 1050); |
| + printf("6\n"); |
| + TraverseFirst(left_asymmetric, right_deep_asymmetric, 65536); |
| + printf("7\n"); |
| + FlattenString(left_asymmetric); |
| + printf("10\n"); |
| + Traverse(flat, left_asymmetric); |
| + printf("11\n"); |
| + FlattenString(right_asymmetric); |
| + printf("12\n"); |
| + Traverse(flat, right_asymmetric); |
| + printf("14\n"); |
| + FlattenString(symmetric); |
| + printf("15\n"); |
| + Traverse(flat, symmetric); |
| + printf("16\n"); |
| + FlattenString(left_deep_asymmetric); |
| + printf("18\n"); |
| } |
| static void VerifyCharacterStream( |
| String* flat_string, String* cons_string) { |
| // Do not want to test ConString traversal on flat string. |
| - CHECK(flat_string->IsFlat()); |
| - CHECK(!flat_string->IsConsString()); |
| + CHECK(flat_string->IsFlat() && !flat_string->IsConsString()); |
| CHECK(cons_string->IsConsString()); |
| // TODO(dcarney) Test stream reset as well. |
| int length = flat_string->length(); |
| @@ -656,30 +650,233 @@ static void VerifyCharacterStream( |
| } |
| -TEST(StringCharacterStreamEdgeCases) { |
| - printf("TestStringCharacterStreamEdgeCases\n"); |
| +static inline void PrintStats(const ConsStringGenerationData& data) { |
| +#ifdef DEBUG |
| +printf( |
| + "%s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d]\n", |
| + "leaves", data.stats_.leaves_, |
| + "empty", data.stats_.empty_leaves_, |
| + "chars", data.stats_.chars_, |
| + "lefts", data.stats_.left_traversals_, |
| + "rights", data.stats_.right_traversals_, |
| + "early_terminations", data.early_terminations_); |
| +#endif |
| +} |
| + |
| + |
| +template<typename BuildString> |
| +void TestStringCharacterStream(BuildString build, int test_cases) { |
| InitializeVM(); |
| Isolate* isolate = Isolate::Current(); |
| HandleScope outer_scope(isolate); |
| ZoneScope zone(Isolate::Current()->runtime_zone(), DELETE_ON_EXIT); |
| - ConsStringGenerationData data; |
| - for (int i = 0; i < kCharacterStreamEdgeCases; i++) { |
| + ConsStringGenerationData data(true); |
| + bool last_test_did_gc = false; |
| + for (int i = 0; i < test_cases; i++) { |
| printf("%d\n", i); |
| - isolate->heap()->CollectAllGarbage( |
| - Heap::kNoGCFlags, "must not allocate in loop"); |
| - AlwaysAllocateScope always_allocate; |
| HandleScope inner_scope(isolate); |
| - Handle<String> cons_string = BuildConsStrings(i, &data); |
| - Handle<String> flat_string = BuildConsStrings(i, &data); |
| + // Build flat version of cons string. |
| + Handle<String> flat_string = build(i, &data); |
| + ConsStringStats flat_string_stats; |
| + AccumulateStats(flat_string, &flat_string_stats); |
| + // Flatten string. |
| FlattenString(flat_string); |
| + // Build unflattened version of cons string to test. |
| + Handle<String> cons_string = build(i, &data); |
| + ConsStringStats cons_string_stats; |
| + AccumulateStats(cons_string, &cons_string_stats); |
| + // Check if gc changed our data structure. |
| + bool broken_by_gc = |
| + cons_string_stats.leaves_ != data.stats_.leaves_ || |
| + cons_string_stats.leaves_ != flat_string_stats.leaves_; |
| + // If gc altered the data structure, do a full collection and retry test. |
| + if (broken_by_gc) { |
| + // Bail if test runs twice. |
| + if (last_test_did_gc) CHECK(false); |
| + printf("forcing gc\n"); |
| + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, "retry test"); |
| + // Retry test. |
| + last_test_did_gc = true; |
| + i--; |
| + continue; |
| + } |
| + last_test_did_gc = false; |
| AssertNoAllocation no_alloc; |
| - CHECK(flat_string->IsConsString() && flat_string->IsFlat()); |
| - VerifyCharacterStream(ConsString::cast(*flat_string)->first(), |
| - *cons_string); |
| + PrintStats(data); |
| + // Full verify of cons string. |
| + cons_string_stats.VerifyEqual(flat_string_stats); |
| + cons_string_stats.VerifyEqual(data.stats_); |
| + VerifyConsString(cons_string, &data); |
| + String* flat_string_ptr = |
| + flat_string->IsConsString() ? |
| + ConsString::cast(*flat_string)->first() : |
| + *flat_string; |
| + VerifyCharacterStream(flat_string_ptr, *cons_string); |
| } |
| } |
| +static const int kCharacterStreamNonRandomCases = 8; |
| + |
| + |
| +static Handle<String> BuildEdgeCaseConsString( |
| + int test_case, ConsStringGenerationData* data) { |
| + data->Reset(); |
| + switch (test_case) { |
| + case 0: |
| + return ConstructBalanced(data, 71); |
| + case 1: |
| + return ConstructLeft(data, 71); |
| + case 2: |
| + return ConstructRight(data, 71); |
| + case 3: |
| + return ConstructLeft(data, 10); |
| + case 4: |
| + return ConstructRight(data, 10); |
| + case 5: |
| + // 2 element balanced tree. |
| + data->stats_.chars_ += data->block(0)->length(); |
| + data->stats_.chars_ += data->block(1)->length(); |
| + data->stats_.leaves_ += 2; |
| + return FACTORY->NewConsString(data->block(0), data->block(1)); |
| + case 6: |
| + // Simple flattened tree. |
| + data->stats_.chars_ += data->block(0)->length(); |
| + data->stats_.chars_ += data->block(1)->length(); |
| + data->stats_.leaves_ += 2; |
| + data->stats_.empty_leaves_ += 1; |
| + { |
| + Handle<String> string = |
| + FACTORY->NewConsString(data->block(0), data->block(1)); |
| + FlattenString(string); |
| + return string; |
| + } |
| + case 7: |
| + // Left node flattened. |
| + data->stats_.chars_ += data->block(0)->length(); |
| + data->stats_.chars_ += data->block(1)->length(); |
| + data->stats_.chars_ += data->block(2)->length(); |
| + data->stats_.leaves_ += 3; |
| + data->stats_.empty_leaves_ += 1; |
| + data->stats_.left_traversals_ += 1; |
| + { |
| + Handle<String> left = |
| + FACTORY->NewConsString(data->block(0), data->block(1)); |
| + FlattenString(left); |
| + return FACTORY->NewConsString(left, data->block(2)); |
| + } |
| + case 8: |
| + // Left node and right node flattened. |
| + data->stats_.chars_ += data->block(0)->length(); |
| + data->stats_.chars_ += data->block(1)->length(); |
| + data->stats_.chars_ += data->block(2)->length(); |
| + data->stats_.chars_ += data->block(3)->length(); |
| + data->stats_.leaves_ += 4; |
| + data->stats_.empty_leaves_ += 2; |
| + data->stats_.left_traversals_ += 1; |
| + data->stats_.right_traversals_ += 1; |
| + { |
| + Handle<String> left = |
| + FACTORY->NewConsString(data->block(0), data->block(1)); |
| + FlattenString(left); |
| + Handle<String> right = |
| + FACTORY->NewConsString(data->block(2), data->block(2)); |
| + FlattenString(right); |
| + return FACTORY->NewConsString(left, right); |
| + } |
| + } |
| + UNREACHABLE(); |
| + return Handle<String>(); |
| +} |
| + |
| + |
| +TEST(StringCharacterStreamEdgeCases) { |
| + printf("TestStringCharacterStreamEdgeCases\n"); |
| + TestStringCharacterStream( |
| + BuildEdgeCaseConsString, kCharacterStreamNonRandomCases); |
| +} |
| + |
| + |
| +static const int kBalances = 3; |
| +static const int kTreeLengths = 4; |
| +static const int kEmptyLeaves = 4; |
| +static const int kUniqueRandomParameters = |
| + kBalances*kTreeLengths*kEmptyLeaves; |
| + |
| + |
| +static void InitializeGenerationData( |
| + int test_case, ConsStringGenerationData* data) { |
| + // Clear the settings and reinit the rng. |
| + data->Reset(); |
| + // Spin up the rng to a known location that is unique per test. |
| + static const int kPerTestJump = 501; |
| + for (int j = 0; j < test_case*kPerTestJump; j++) { |
| + data->rng_.next(); |
| + } |
| + // Choose balanced, left or right heavy trees. |
| + switch (test_case % kBalances) { |
| + case 0: |
| + // Nothing to do. Already balanced. |
| + break; |
| + case 1: |
| + // Left balanced. |
| + data->leftness_ = 0.90; |
| + data->rightness_ = 0.15; |
| + break; |
| + case 2: |
| + // Right balanced. |
| + data->leftness_ = 0.15; |
| + data->rightness_ = 0.90; |
| + break; |
| + default: |
| + UNREACHABLE(); |
| + break; |
| + } |
| + // Must remove the influence of the above decision. |
| + test_case /= kBalances; |
| + // Choose tree length. |
| + switch (test_case % kTreeLengths) { |
| + case 0: |
| + data->max_leaves_ = 16; |
| + data->early_termination_threshold_ = 0.2; |
| + break; |
| + case 1: |
| + data->max_leaves_ = 50; |
| + data->early_termination_threshold_ = 0.05; |
| + break; |
| + case 2: |
| + data->max_leaves_ = 500; |
| + data->early_termination_threshold_ = 0.03; |
| + break; |
| + case 3: |
| + data->max_leaves_ = 5000; |
| + data->early_termination_threshold_ = 0.001; |
| + break; |
| + default: |
| + UNREACHABLE(); |
| + break; |
| + } |
| + // Must remove the influence of the above decision. |
| + test_case /= kTreeLengths; |
| + // Choose how much we allow empty nodes, including not at all. |
| + data->empty_leaf_threshold_ = |
| + 0.03 * static_cast<double>(test_case % kEmptyLeaves); |
| +} |
| + |
| + |
| +static Handle<String> BuildRandomConsString( |
| + int test_case, ConsStringGenerationData* data) { |
| + InitializeGenerationData(test_case, data); |
| + return ConstructRandomString(data, 200); |
| +} |
| + |
| + |
| +TEST(StringCharacterStreamRandom) { |
|
Michael Starzinger
2012/12/14 09:23:07
This test is failing on the GC Stress builder.
ht
|
| + printf("StringCharacterStreamRandom\n"); |
| + TestStringCharacterStream(BuildRandomConsString, kUniqueRandomParameters*7); |
| +} |
| + |
| + |
| static const int DEEP_ASCII_DEPTH = 100000; |