| /** @file | |
| Unit tests for Http2DependencyTree | |
| @section license License | |
| Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you under the Apache License, Version 2.0 (the | |
| "License"); you may not use this file except in compliance | |
| with the License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| #include <catch2/catch_test_macros.hpp> | |
| #include <iostream> | |
| #include <cstring> | |
| #include <sstream> | |
| #include "proxy/http2/Http2DependencyTree.h" | |
| using namespace std; | |
| using Tree = Http2DependencyTree::Tree<std::string *>; | |
| using Node = Http2DependencyTree::Node; | |
| /** | |
| * Exclusive Dependency Creation | |
| * | |
| * A A | |
| * / \ => | | |
| * B C D | |
| * / \ | |
| * B C | |
| */ | |
| TEST_CASE("Http2DependencyTree_1", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"); | |
| tree->add(0, 1, 0, false, &b); | |
| tree->add(0, 3, 0, false, &c); | |
| Node *node_a = tree->find(0); | |
| Node *node_b = tree->find(1); | |
| Node *node_c = tree->find(3); | |
| REQUIRE(node_b->parent == node_a); | |
| REQUIRE(node_c->parent == node_a); | |
| // Add node with exclusive flag | |
| tree->add(0, 5, 0, true, &d); | |
| Node *node_d = tree->find(5); | |
| REQUIRE(node_d->parent == node_a); | |
| REQUIRE(node_b->parent == node_d); | |
| REQUIRE(node_c->parent == node_d); | |
| } | |
| /** | |
| * Reprioritization (non-exclusive) | |
| * | |
| * x x | |
| * | | | |
| * A D | |
| * / \ / \ | |
| * B C ==> F A | |
| * / \ / \ | |
| * D E B C | |
| * | | | |
| * F E | |
| */ | |
| TEST_CASE("Http2DependencyTree_2", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"); | |
| tree->add(0, 1, 0, false, &a); | |
| tree->add(1, 3, 0, false, &b); | |
| tree->add(1, 5, 0, false, &c); | |
| tree->add(5, 7, 0, false, &d); | |
| tree->add(5, 9, 0, false, &e); | |
| tree->add(7, 11, 0, false, &f); | |
| tree->reprioritize(1, 7, false); | |
| Node *node_x = tree->find(0); | |
| Node *node_a = tree->find(1); | |
| Node *node_d = tree->find(7); | |
| Node *node_f = tree->find(11); | |
| REQUIRE(node_a->parent == node_d); | |
| REQUIRE(node_d->parent == node_x); | |
| REQUIRE(node_f->parent == node_d); | |
| } | |
| /** | |
| * Reprioritization (exclusive) | |
| * | |
| * x x | |
| * | | | |
| * A D | |
| * / \ | | |
| * B C ==> A | |
| * / \ /|\ | |
| * D E B C F | |
| * | | | |
| * F E | |
| */ | |
| TEST_CASE("Http2DependencyTree_3", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"); | |
| tree->add(0, 1, 0, false, &a); | |
| tree->add(1, 3, 0, false, &b); | |
| tree->add(1, 5, 0, false, &c); | |
| tree->add(5, 7, 0, false, &d); | |
| tree->add(5, 9, 0, false, &e); | |
| tree->add(7, 11, 0, false, &f); | |
| tree->reprioritize(1, 7, true); | |
| Node *node_x = tree->find(0); | |
| Node *node_a = tree->find(1); | |
| Node *node_d = tree->find(7); | |
| Node *node_f = tree->find(11); | |
| REQUIRE(node_a->parent == node_d); | |
| REQUIRE(node_d->parent == node_x); | |
| REQUIRE(node_f->parent == node_a); | |
| } | |
| /** | |
| * Only One Node Tree | |
| * ROOT | |
| * / | |
| * A(1) | |
| */ | |
| TEST_CASE("Http2DependencyTree_4", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"); | |
| tree->add(0, 1, 0, false, &a); | |
| Node *node_a = tree->find(1); | |
| REQUIRE(tree->top() == nullptr); | |
| tree->activate(node_a); | |
| REQUIRE(tree->top() == node_a); | |
| tree->deactivate(node_a, 0); | |
| REQUIRE(tree->top() == nullptr); | |
| } | |
| /** | |
| * Simple Tree | |
| * ROOT | |
| * / | |
| * A(3) | |
| * / | |
| * B(5) | |
| * | |
| */ | |
| TEST_CASE("Http2DependencyTree_5", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"); | |
| tree->add(0, 3, 15, false, &a); | |
| tree->add(3, 5, 15, false, &b); | |
| Node *node_a = tree->find(3); | |
| Node *node_b = tree->find(5); | |
| REQUIRE(tree->top() == nullptr); | |
| tree->activate(node_a); | |
| tree->activate(node_b); | |
| REQUIRE(tree->top() == node_a); | |
| tree->deactivate(node_a, 0); | |
| REQUIRE(tree->top() == node_b); | |
| } | |
| /** | |
| * Basic Tree | |
| * ROOT | |
| * / \ | |
| * A(3) D(9) | |
| * / \ | |
| * B(5) C(7) | |
| * | |
| */ | |
| TEST_CASE("Http2DependencyTree_6", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"); | |
| // NOTE, weight is actual weight - 1 | |
| tree->add(0, 3, 20, false, &a); // node_a is unused | |
| Node *node_b = tree->add(3, 5, 10, false, &b); | |
| Node *node_c = tree->add(3, 7, 10, false, &c); | |
| Node *node_d = tree->add(0, 9, 20, false, &d); | |
| // Activate B, C and D | |
| tree->activate(node_b); | |
| tree->activate(node_c); | |
| tree->activate(node_d); | |
| ostringstream oss; | |
| for (int i = 0; i < 90; ++i) { | |
| Node *node = tree->top(); | |
| oss << static_cast<string *>(node->t)->c_str(); | |
| tree->update(node, 100); | |
| } | |
| const string expect = "BDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBDCDBD"; | |
| REQUIRE(oss.str() == expect); | |
| } | |
| /** | |
| * Tree of Chrome 50 | |
| * | |
| * ROOT | |
| * / | \ | |
| * A(3) B(5) ... I(19) | |
| * | |
| */ | |
| TEST_CASE("Http2DependencyTree_Chrome_50", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"), g("G"), h("H"), i("I"); | |
| Node *node_a = tree->add(0, 3, 255, false, &a); | |
| Node *node_b = tree->add(0, 5, 255, false, &b); | |
| Node *node_c = tree->add(0, 7, 255, false, &c); | |
| Node *node_d = tree->add(0, 9, 182, false, &d); | |
| Node *node_e = tree->add(0, 11, 182, false, &e); | |
| Node *node_f = tree->add(0, 13, 182, false, &f); | |
| Node *node_g = tree->add(0, 15, 146, false, &g); | |
| Node *node_h = tree->add(0, 17, 146, false, &h); | |
| Node *node_i = tree->add(0, 19, 146, false, &i); | |
| // Activate nodes from A to I | |
| tree->activate(node_a); | |
| tree->activate(node_b); | |
| tree->activate(node_c); | |
| tree->activate(node_d); | |
| tree->activate(node_e); | |
| tree->activate(node_f); | |
| tree->activate(node_g); | |
| tree->activate(node_h); | |
| tree->activate(node_i); | |
| ostringstream oss; | |
| for (int index = 0; index < 108; ++index) { | |
| Node *node = tree->top(); | |
| oss << static_cast<string *>(node->t)->c_str(); | |
| tree->update(node, 16375); | |
| } | |
| const string expect = | |
| "ABCDEFGHIABCDEFGHIABCDEFABCGHIABCDEFABCGHIDEFABCGHIDEFABCABCDEFGHIABCDEFABCGHIABCDEFABCGHIDEFABCGHIDEFABCABC"; | |
| REQUIRE(oss.str() == expect); | |
| } | |
| /** | |
| * Tree of Chrome 51 | |
| * | |
| * ROOT | |
| * | | |
| * A(3) | |
| * | | |
| * B(5) | |
| * . | |
| * . | |
| * . | |
| * I(19) | |
| * | |
| */ | |
| TEST_CASE("Http2DependencyTree_Chrome_51", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"), g("G"), h("H"), i("I"); | |
| Node *node_a = tree->add(0, 3, 255, false, &a); | |
| Node *node_b = tree->add(3, 5, 255, false, &b); | |
| Node *node_c = tree->add(5, 7, 255, false, &c); | |
| Node *node_d = tree->add(7, 9, 182, false, &d); | |
| Node *node_e = tree->add(9, 11, 182, false, &e); | |
| Node *node_f = tree->add(11, 13, 182, false, &f); | |
| Node *node_g = tree->add(13, 15, 146, false, &g); | |
| Node *node_h = tree->add(15, 17, 146, false, &h); | |
| Node *node_i = tree->add(17, 19, 146, false, &i); | |
| // Activate nodes A, C, E, G, and I | |
| tree->activate(node_a); | |
| tree->activate(node_c); | |
| tree->activate(node_e); | |
| tree->activate(node_g); | |
| tree->activate(node_i); | |
| ostringstream oss; | |
| for (int index = 0; index < 9; ++index) { | |
| Node *node = tree->top(); | |
| if (node != nullptr) { | |
| oss << static_cast<string *>(node->t)->c_str(); | |
| tree->deactivate(node, 16384); | |
| tree->remove(node); | |
| } | |
| } | |
| // Activate nodes B, D, F, and H | |
| tree->activate(node_b); | |
| tree->activate(node_d); | |
| tree->activate(node_f); | |
| tree->activate(node_h); | |
| for (int index = 0; index < 9; ++index) { | |
| Node *node = tree->top(); | |
| if (node != nullptr) { | |
| oss << static_cast<string *>(node->t)->c_str(); | |
| tree->deactivate(node, 16384); | |
| tree->remove(node); | |
| } | |
| } | |
| const string expect = "ACEGIBDFH"; | |
| REQUIRE(oss.str() == expect); | |
| } | |
| /** | |
| * Removing Node from tree 1 | |
| * | |
| * ROOT | |
| * | | |
| * A(3) | |
| * / \ | |
| * B(5) C(7) | |
| * | |
| */ | |
| TEST_CASE("Http2DependencyTree_remove_1", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"); | |
| // NOTE, weight is actual weight - 1 | |
| Node *node_a = tree->add(0, 3, 30, false, &a); | |
| Node *node_b = tree->add(3, 5, 20, false, &b); | |
| Node *node_c = tree->add(3, 7, 10, false, &c); | |
| // Activate A, B, and C | |
| tree->activate(node_a); | |
| tree->activate(node_b); | |
| tree->activate(node_c); | |
| Node *top_node = nullptr; | |
| // Deactivate A and try to remove | |
| top_node = tree->top(); | |
| REQUIRE(top_node == node_a); | |
| tree->deactivate(node_a, 16); | |
| tree->remove(node_a); | |
| REQUIRE(tree->find(3) == nullptr); | |
| // Deactivate B and try to remove | |
| top_node = tree->top(); | |
| REQUIRE(top_node == node_b); | |
| tree->deactivate(node_b, 16); | |
| tree->remove(node_b); | |
| REQUIRE(tree->find(5) == nullptr); | |
| // Deactivate C and try to remove | |
| top_node = tree->top(); | |
| REQUIRE(top_node == node_c); | |
| tree->deactivate(node_c, 16); | |
| tree->remove(node_c); | |
| REQUIRE(tree->find(7) == nullptr); | |
| } | |
| /** | |
| * Removing Node from tree 2 | |
| * | |
| * ROOT | |
| * | | |
| * A(3) | |
| * | | |
| * B(5) | |
| * | | |
| * C(7) | |
| */ | |
| TEST_CASE("Http2DependencyTree_remove_2", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"); | |
| // NOTE, weight is actual weight - 1 | |
| Node *node_a = tree->add(0, 3, 20, false, &a); | |
| Node *node_b = tree->add(3, 5, 10, false, &b); | |
| Node *node_c = tree->add(5, 7, 10, false, &c); | |
| // Activate, deactivate, and remove C | |
| tree->activate(node_c); | |
| REQUIRE(tree->top() == node_c); | |
| tree->deactivate(node_c, 16384); | |
| tree->remove(node_c); | |
| // Activate, deactivate, and remove A | |
| tree->activate(node_a); | |
| REQUIRE(tree->top() == node_a); | |
| tree->deactivate(node_a, 16384); | |
| tree->remove(node_a); | |
| // Activate, deactivate, and remove B | |
| tree->activate(node_b); | |
| REQUIRE(tree->top() == node_b); | |
| tree->deactivate(node_b, 16384); | |
| tree->remove(node_b); | |
| REQUIRE(tree->top() == nullptr); | |
| REQUIRE(tree->find(3) == nullptr); | |
| REQUIRE(tree->find(5) == nullptr); | |
| REQUIRE(tree->find(7) == nullptr); | |
| } | |
| /** | |
| * Exclusive Dependency Creation | |
| * | |
| * A A | |
| * / \ => | | |
| * B C D | |
| * / \ | |
| * B C | |
| */ | |
| TEST_CASE("Http2DependencyTree_exclusive_node", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"); | |
| Node *B = tree->add(0, 1, 0, false, &b); | |
| tree->add(0, 3, 0, false, &c); | |
| tree->activate(B); | |
| // Add node with exclusive flag | |
| tree->add(0, 5, 0, true, &d); | |
| tree->deactivate(B, 0); | |
| tree->remove(B); | |
| REQUIRE(tree->top() == nullptr); | |
| } | |
| /** test for reprioritize with active node | |
| * | |
| * root root root | |
| * / \ / \ (remove A) / \ | |
| * A B =======> C B =======> C B | |
| * \ / | |
| * C A | |
| * | |
| */ | |
| TEST_CASE("Http2DependencyTree_reprioritize", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"); | |
| Node *A = tree->add(0, 7, 70, false, &a); | |
| Node *B = tree->add(0, 3, 10, false, &b); | |
| Node *C = tree->add(3, 5, 30, false, &c); | |
| tree->activate(A); | |
| tree->activate(B); | |
| tree->activate(C); | |
| tree->reprioritize(A, 5, false); | |
| tree->deactivate(A, 0); | |
| tree->remove(A); | |
| REQUIRE(tree->top()->t != nullptr); | |
| } | |
| /** | |
| * Reprioritization (exclusive) | |
| * | |
| * x x | |
| * | | | |
| * A D | |
| * / \ | | |
| * B C ==> A | |
| * / \ /|\ | |
| * D E B C F | |
| * | | | |
| * F E | |
| */ | |
| TEST_CASE("Http2DependencyTree_reprioritize_2", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"); | |
| tree->add(0, 1, 0, false, &a); | |
| tree->add(1, 3, 0, false, &b); | |
| tree->add(1, 5, 0, false, &c); | |
| tree->add(5, 7, 0, false, &d); | |
| tree->add(5, 9, 0, false, &e); | |
| tree->add(7, 11, 0, false, &f); | |
| Node *node_x = tree->find(0); | |
| Node *node_a = tree->find(1); | |
| Node *node_b = tree->find(3); | |
| Node *node_d = tree->find(7); | |
| tree->activate(node_b); | |
| REQUIRE(node_x->queue->in(node_a->entry.get())); | |
| tree->reprioritize(1, 7, true); | |
| REQUIRE(!node_x->queue->in(node_a->entry.get())); | |
| REQUIRE(node_x->queue->in(node_d->entry.get())); | |
| REQUIRE(node_d->queue->in(node_a->entry.get())); | |
| } | |
| /** | |
| * Reprioritization (exclusive) | |
| * | |
| * x x | |
| * | | | |
| * A D | |
| * / \ | | |
| * B C ==> A | |
| * / \ /|\ | |
| * D E B C F | |
| * | | | |
| * F E | |
| */ | |
| TEST_CASE("Http2DependencyTree_reprioritize_3", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"); | |
| tree->add(0, 1, 0, false, &a); | |
| tree->add(1, 3, 0, false, &b); | |
| tree->add(1, 5, 0, false, &c); | |
| tree->add(5, 7, 0, false, &d); | |
| tree->add(5, 9, 0, false, &e); | |
| tree->add(7, 11, 0, false, &f); | |
| Node *node_x = tree->find(0); | |
| Node *node_a = tree->find(1); | |
| Node *node_c = tree->find(5); | |
| Node *node_d = tree->find(7); | |
| Node *node_f = tree->find(11); | |
| tree->activate(node_f); | |
| tree->reprioritize(1, 7, true); | |
| REQUIRE(node_a->queue->in(node_f->entry.get())); | |
| REQUIRE(node_d->queue->in(node_a->entry.get())); | |
| REQUIRE(node_x->queue->in(node_d->entry.get())); | |
| REQUIRE(!node_a->queue->in(node_c->entry.get())); | |
| REQUIRE(node_c->queue->empty()); | |
| } | |
| /** | |
| * https://github.com/apache/trafficserver/issues/4057 | |
| * Reprioritization to root | |
| * | |
| * x x | |
| * | / \ | |
| * A A D | |
| * / \ / \ | | |
| * B C ==> B C F | |
| * / \ | | |
| * D E E | |
| * | | |
| * F | |
| */ | |
| TEST_CASE("Http2DependencyTree_reprioritize_4", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"); | |
| tree->add(0, 1, 0, false, &a); | |
| tree->add(1, 3, 0, false, &b); | |
| tree->add(1, 5, 0, false, &c); | |
| tree->add(5, 7, 0, false, &d); | |
| tree->add(5, 9, 0, false, &e); | |
| tree->add(7, 11, 0, false, &f); | |
| Node *node_x = tree->find(0); | |
| Node *node_a = tree->find(1); | |
| Node *node_c = tree->find(5); | |
| Node *node_d = tree->find(7); | |
| Node *node_f = tree->find(11); | |
| tree->activate(node_f); | |
| tree->reprioritize(7, 0, false); | |
| REQUIRE(!node_a->queue->in(node_f->entry.get())); | |
| REQUIRE(node_d->queue->in(node_f->entry.get())); | |
| REQUIRE(node_x->queue->in(node_d->entry.get())); | |
| REQUIRE(!node_a->queue->in(node_c->entry.get())); | |
| REQUIRE(node_c->queue->empty()); | |
| } | |
| /** | |
| * https://github.com/apache/trafficserver/issues/4057 | |
| * Reprioritization to unrelated node | |
| * | |
| * x x | |
| * | | | |
| * A A | |
| * / \ / \ | |
| * B C ==> B C | |
| * / \ | | | |
| * D E D E | |
| * | | | |
| * F F | |
| */ | |
| TEST_CASE("Http2DependencyTree_reprioritize_5", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"), f("F"); | |
| tree->add(0, 1, 0, false, &a); | |
| tree->add(1, 3, 0, false, &b); | |
| tree->add(1, 5, 0, false, &c); | |
| tree->add(5, 7, 0, false, &d); | |
| tree->add(5, 9, 0, false, &e); | |
| tree->add(7, 11, 0, false, &f); | |
| Node *node_x = tree->find(0); | |
| Node *node_a = tree->find(1); | |
| Node *node_b = tree->find(3); | |
| Node *node_c = tree->find(5); | |
| Node *node_d = tree->find(7); | |
| Node *node_f = tree->find(11); | |
| tree->activate(node_f); | |
| tree->reprioritize(7, 3, false); | |
| REQUIRE(node_a->queue->in(node_b->entry.get())); | |
| REQUIRE(node_b->queue->in(node_d->entry.get())); | |
| REQUIRE(!node_c->queue->in(node_d->entry.get())); | |
| REQUIRE(node_x->queue->in(node_a->entry.get())); | |
| REQUIRE(!node_a->queue->in(node_c->entry.get())); | |
| REQUIRE(node_c->queue->empty()); | |
| } | |
| /** test for https://github.com/apache/trafficserver/issues/2268 | |
| * | |
| * root root root | |
| * / =====> / \ =======> / \ | |
| * A A shadow A shadow | |
| * \ \ | |
| * B B | |
| * \ | |
| * C | |
| * | |
| * root root | |
| * / \ / | |
| * ======> A shadow =======> A | |
| * \ | |
| * C | |
| */ | |
| TEST_CASE("Http2DependencyTree_insert_with_empty_parent", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"); | |
| tree->add(0, 3, 20, false, &a); | |
| Node *b_n = tree->add(9, 7, 30, true, &b); | |
| REQUIRE(b_n->parent->id == 9); | |
| REQUIRE(tree->find(9) == nullptr); | |
| REQUIRE(tree->find_shadow(9)->is_shadow() == true); | |
| Node *c_n = tree->add(7, 11, 30, false, &c); | |
| tree->remove(b_n); | |
| REQUIRE(c_n->parent->id == 9); | |
| REQUIRE(tree->find(7) == nullptr); | |
| REQUIRE(tree->find_shadow(9)->is_shadow() == true); | |
| tree->remove(c_n); | |
| REQUIRE(tree->find_shadow(9) == nullptr); | |
| } | |
| /** test for https://github.com/apache/trafficserver/issues/2268 | |
| * | |
| * root root root root | |
| * / =====> / \ =======> / \ =======> / \ | |
| * A A shadow A B A B | |
| * \ \ | |
| * B shadow | |
| */ | |
| TEST_CASE("Http2DependencyTree_shadow_reprioritize", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"); | |
| tree->add(0, 3, 20, false, &a); | |
| tree->add(9, 7, 30, true, &b); | |
| Node *s_n = tree->find_shadow(9); | |
| REQUIRE(s_n != nullptr); | |
| REQUIRE(s_n->is_shadow() == true); | |
| tree->reprioritize(s_n, 7, false); | |
| REQUIRE(tree->find_shadow(9) == nullptr); | |
| } | |
| /** Test for https://github.com/apache/trafficserver/pull/4212 | |
| * | |
| * Add child to parent that has already completed. | |
| * | |
| * root root root root root | |
| * | | | | | | |
| * A ====> A ====> A ====> A ====> A | |
| * | | | | |
| * B C E | |
| * | | |
| * D | |
| */ | |
| TEST_CASE("Http2DependencyTree_delete_parent_before_child_arrives", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"); | |
| tree->add(0, 3, 20, false, &a); | |
| Node *node_b = tree->add(3, 5, 30, true, &b); | |
| tree->remove(node_b); | |
| // Tree should remember B, so C will be added to B's ancestor | |
| Node *node_c = tree->add(5, 7, 20, false, &c); | |
| REQUIRE(node_c->parent->id == 3); | |
| // See if it remembers two missing ancestors | |
| Node *node_d = tree->add(7, 9, 20, false, &d); | |
| tree->remove(node_c); | |
| tree->remove(node_d); | |
| Node *node_e = tree->add(9, 11, 30, false, &e); | |
| REQUIRE(node_e->parent->id == 3); | |
| } | |
| /** Test for https://github.com/apache/trafficserver/pull/4212 | |
| * | |
| * Make sure priority nodes stick around | |
| * | |
| * root root | |
| * / | \ / | \ | |
| * P1 P2 P3 ====> P1 P2 P3 | |
| * | | | | | | |
| * A B C B C | |
| * | | | |
| * D D | |
| */ | |
| TEST_CASE("Http2DependencyTree_handle_priority_nodes", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"), d("D"), e("E"); | |
| // P1 node | |
| tree->add(0, 3, 20, false, nullptr); | |
| // P2 node | |
| tree->add(0, 5, 20, false, nullptr); | |
| // P3 node | |
| tree->add(0, 7, 20, false, nullptr); | |
| Node *node_a = tree->add(3, 9, 30, true, &a); | |
| Node *node_b = tree->add(5, 11, 30, true, &b); | |
| Node *node_c = tree->add(7, 13, 30, true, &c); | |
| Node *node_d = tree->add(11, 15, 30, true, &d); | |
| REQUIRE(node_a->parent->id == 3); | |
| REQUIRE(node_b->parent->id == 5); | |
| REQUIRE(node_c->parent->id == 7); | |
| REQUIRE(node_d->parent->id == 11); | |
| // Deleting the children should not make the priority node go away | |
| tree->remove(node_a); | |
| Node *node_p1 = tree->find(3); | |
| REQUIRE(node_p1 != nullptr); | |
| } | |
| /** | |
| * Shadow nodes should reprioritize when they vivify | |
| * | |
| * root root root | |
| * / \ | | | |
| * A Shadow ====> A ====> A | |
| * | | | | |
| * B C(was shadow) C | |
| * | | |
| * B | |
| */ | |
| TEST_CASE("Http2DependencyTree_reprioritize_shadow_node", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"); | |
| tree->add(0, 3, 20, false, &a); | |
| // 7 should be created as a shadow node | |
| tree->add(7, 5, 20, false, &b); | |
| Node *b_n = tree->find(5); | |
| Node *c_n = tree->find(7); | |
| Node *c_shadow_n = tree->find_shadow(7); | |
| REQUIRE(b_n != nullptr); | |
| REQUIRE(b_n->parent->id == 7); | |
| REQUIRE(c_n == nullptr); | |
| REQUIRE(c_shadow_n != nullptr); | |
| REQUIRE(c_shadow_n->parent->id == 0); | |
| // Now populate the shadow | |
| tree->add(3, 7, 30, false, &c); | |
| c_n = tree->find(7); | |
| REQUIRE(c_n != nullptr); | |
| REQUIRE(c_n->parent->id); | |
| REQUIRE(c_n->weight == 30); | |
| // C should still exist when its child goes away | |
| tree->remove(b_n); | |
| c_n = tree->find(7); | |
| REQUIRE(c_n != nullptr); | |
| } | |
| TEST_CASE("Http2DependencyTree_missing_parent", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"), b("B"), c("C"); | |
| tree->add(0, 3, 20, false, &a); | |
| tree->add(5, 7, 30, true, &b); | |
| Node *c_n = tree->find(5); | |
| Node *c_shadow_n = tree->find_shadow(5); | |
| REQUIRE(c_n == nullptr); | |
| REQUIRE(c_shadow_n != nullptr); | |
| REQUIRE(c_shadow_n->is_shadow() == true); | |
| tree->add(0, 5, 15, false, &c); | |
| c_n = tree->find(5); | |
| REQUIRE(c_n != nullptr); | |
| REQUIRE(c_n->is_shadow() == false); | |
| REQUIRE(c_n->point == 5); | |
| REQUIRE(c_n->weight == 15); | |
| } | |
| TEST_CASE("Http2DependencyTree_max_depth", "[http2][Http2DependencyTree]") | |
| { | |
| auto tree = std::make_unique<Tree>(100); | |
| string a("A"); | |
| for (int i = 0; i < 100; ++i) { | |
| tree->add(i, i + 1, 16, false, &a); | |
| } | |
| Node *node = tree->find(100); | |
| Node *leaf = tree->find(99); | |
| REQUIRE(node->parent->id == 0); | |
| REQUIRE(leaf != nullptr); | |
| REQUIRE(leaf->parent->id != 0); | |
| } |