testFindSCCs.cpp (4839B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 */ 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #include <stdarg.h> 9 #include <string.h> 10 11 #include "gc/FindSCCs.h" 12 #include "jsapi-tests/tests.h" 13 14 static const unsigned MaxVertices = 10; 15 16 using js::gc::ComponentFinder; 17 using js::gc::GraphNodeBase; 18 19 struct TestNode : public GraphNodeBase<TestNode> { 20 unsigned index; 21 }; 22 23 using TestComponentFinder = ComponentFinder<TestNode>; 24 25 MOZ_RUNINIT static TestNode Vertex[MaxVertices]; 26 27 BEGIN_TEST(testFindSCCs) { 28 // no vertices 29 30 setup(0); 31 run(); 32 CHECK(end()); 33 34 // no edges 35 36 setup(1); 37 run(); 38 CHECK(group(0, -1)); 39 CHECK(end()); 40 41 setup(3); 42 run(); 43 CHECK(group(2, -1)); 44 CHECK(group(1, -1)); 45 CHECK(group(0, -1)); 46 CHECK(end()); 47 48 // linear 49 50 setup(3); 51 CHECK(edge(0, 1)); 52 CHECK(edge(1, 2)); 53 run(); 54 CHECK(group(0, -1)); 55 CHECK(group(1, -1)); 56 CHECK(group(2, -1)); 57 CHECK(end()); 58 59 // tree 60 61 setup(3); 62 CHECK(edge(0, 1)); 63 CHECK(edge(0, 2)); 64 run(); 65 CHECK(group(0, -1)); 66 if (resultsList && resultsList->index == 1) { 67 CHECK(group(1, -1)); 68 CHECK(group(2, -1)); 69 } else { 70 CHECK(group(2, -1)); 71 CHECK(group(1, -1)); 72 } 73 CHECK(end()); 74 75 // cycles 76 77 setup(3); 78 CHECK(edge(0, 1)); 79 CHECK(edge(1, 2)); 80 CHECK(edge(2, 0)); 81 run(); 82 CHECK(group(0, 1, 2, -1)); 83 CHECK(end()); 84 85 setup(4); 86 CHECK(edge(0, 1)); 87 CHECK(edge(1, 2)); 88 CHECK(edge(2, 1)); 89 CHECK(edge(2, 3)); 90 run(); 91 CHECK(group(0, -1)); 92 CHECK(group(1, 2, -1)); 93 CHECK(group(3, -1)); 94 CHECK(end()); 95 96 // remaining 97 98 setup(2); 99 CHECK(edge(0, 1)); 100 run(); 101 CHECK(remaining(0, 1, -1)); 102 CHECK(end()); 103 104 setup(2); 105 CHECK(edge(0, 1)); 106 run(); 107 CHECK(group(0, -1)); 108 CHECK(remaining(1, -1)); 109 CHECK(end()); 110 111 setup(2); 112 CHECK(edge(0, 1)); 113 run(); 114 CHECK(group(0, -1)); 115 CHECK(group(1, -1)); 116 CHECK(remaining(-1)); 117 CHECK(end()); 118 119 return true; 120 } 121 122 unsigned vertex_count; 123 TestComponentFinder* finder; 124 TestNode* resultsList; 125 126 void setup(unsigned count) { 127 vertex_count = count; 128 for (unsigned i = 0; i < MaxVertices; ++i) { 129 TestNode& v = Vertex[i]; 130 v.gcGraphEdges.clear(); 131 v.gcNextGraphNode = nullptr; 132 v.index = i; 133 } 134 } 135 136 bool edge(unsigned src_index, unsigned dest_index) { 137 return Vertex[src_index].gcGraphEdges.put(&Vertex[dest_index]); 138 } 139 140 void run() { 141 finder = new TestComponentFinder(cx); 142 for (unsigned i = 0; i < vertex_count; ++i) { 143 finder->addNode(&Vertex[i]); 144 } 145 resultsList = finder->getResultsList(); 146 } 147 148 bool group(int vertex, ...) { 149 TestNode* v = resultsList; 150 151 va_list ap; 152 va_start(ap, vertex); 153 while (vertex != -1) { 154 CHECK(v != nullptr); 155 CHECK(v->index == unsigned(vertex)); 156 v = v->nextNodeInGroup(); 157 vertex = va_arg(ap, int); 158 } 159 va_end(ap); 160 161 CHECK(v == nullptr); 162 resultsList = resultsList->nextGroup(); 163 return true; 164 } 165 166 bool remaining(int vertex, ...) { 167 TestNode* v = resultsList; 168 169 va_list ap; 170 va_start(ap, vertex); 171 while (vertex != -1) { 172 CHECK(v != nullptr); 173 CHECK(v->index == unsigned(vertex)); 174 v = (TestNode*)v->gcNextGraphNode; 175 vertex = va_arg(ap, int); 176 } 177 va_end(ap); 178 179 CHECK(v == nullptr); 180 resultsList = nullptr; 181 return true; 182 } 183 184 bool end() { 185 CHECK(resultsList == nullptr); 186 187 delete finder; 188 finder = nullptr; 189 return true; 190 } 191 END_TEST(testFindSCCs) 192 193 BEGIN_TEST(testFindSCCsStackLimit) { 194 /* 195 * Test what happens if recusion causes the stack to become full while 196 * traversing the graph. 197 * 198 * The test case is a large number of vertices, almost all of which are 199 * arranged in a linear chain. The last few are left unlinked to exercise 200 * adding vertices after the stack full condition has already been detected. 201 * 202 * Such an arrangement with no cycles would normally result in one group for 203 * each vertex, but since the stack is exhasted in processing a single group 204 * is returned containing all the vertices. 205 */ 206 const unsigned max = 1000000; 207 const unsigned initial = 10; 208 209 TestNode* vertices = new TestNode[max](); 210 for (unsigned i = initial; i < (max - 10); ++i) { 211 CHECK(vertices[i].gcGraphEdges.put(&vertices[i + 1])); 212 } 213 214 TestComponentFinder finder(cx); 215 for (unsigned i = 0; i < max; ++i) { 216 finder.addNode(&vertices[i]); 217 } 218 219 TestNode* r = finder.getResultsList(); 220 CHECK(r); 221 TestNode* v = r; 222 223 unsigned count = 0; 224 while (v) { 225 ++count; 226 v = v->nextNodeInGroup(); 227 } 228 CHECK(count == max - initial); 229 230 count = 0; 231 v = r->nextGroup(); 232 while (v) { 233 ++count; 234 CHECK(!v->nextNodeInGroup()); 235 v = v->nextGroup(); 236 } 237 238 delete[] vertices; 239 return true; 240 } 241 END_TEST(testFindSCCsStackLimit)