summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testFindSCCs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsapi-tests/testFindSCCs.cpp')
-rw-r--r--js/src/jsapi-tests/testFindSCCs.cpp242
1 files changed, 242 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testFindSCCs.cpp b/js/src/jsapi-tests/testFindSCCs.cpp
new file mode 100644
index 0000000000..3353e3c910
--- /dev/null
+++ b/js/src/jsapi-tests/testFindSCCs.cpp
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdarg.h>
+#include <string.h>
+
+#include "gc/FindSCCs.h"
+#include "jsapi-tests/tests.h"
+
+static const unsigned MaxVertices = 10;
+
+using js::gc::ComponentFinder;
+using js::gc::GraphNodeBase;
+
+struct TestNode : public GraphNodeBase<TestNode> {
+ unsigned index;
+};
+
+using TestComponentFinder = ComponentFinder<TestNode>;
+
+static TestNode Vertex[MaxVertices];
+
+BEGIN_TEST(testFindSCCs) {
+ // no vertices
+
+ setup(0);
+ run();
+ CHECK(end());
+
+ // no edges
+
+ setup(1);
+ run();
+ CHECK(group(0, -1));
+ CHECK(end());
+
+ setup(3);
+ run();
+ CHECK(group(2, -1));
+ CHECK(group(1, -1));
+ CHECK(group(0, -1));
+ CHECK(end());
+
+ // linear
+
+ setup(3);
+ CHECK(edge(0, 1));
+ CHECK(edge(1, 2));
+ run();
+ CHECK(group(0, -1));
+ CHECK(group(1, -1));
+ CHECK(group(2, -1));
+ CHECK(end());
+
+ // tree
+
+ setup(3);
+ CHECK(edge(0, 1));
+ CHECK(edge(0, 2));
+ run();
+ CHECK(group(0, -1));
+ if (resultsList && resultsList->index == 1) {
+ CHECK(group(1, -1));
+ CHECK(group(2, -1));
+ } else {
+ CHECK(group(2, -1));
+ CHECK(group(1, -1));
+ }
+ CHECK(end());
+
+ // cycles
+
+ setup(3);
+ CHECK(edge(0, 1));
+ CHECK(edge(1, 2));
+ CHECK(edge(2, 0));
+ run();
+ CHECK(group(0, 1, 2, -1));
+ CHECK(end());
+
+ setup(4);
+ CHECK(edge(0, 1));
+ CHECK(edge(1, 2));
+ CHECK(edge(2, 1));
+ CHECK(edge(2, 3));
+ run();
+ CHECK(group(0, -1));
+ CHECK(group(1, 2, -1));
+ CHECK(group(3, -1));
+ CHECK(end());
+
+ // remaining
+
+ setup(2);
+ CHECK(edge(0, 1));
+ run();
+ CHECK(remaining(0, 1, -1));
+ CHECK(end());
+
+ setup(2);
+ CHECK(edge(0, 1));
+ run();
+ CHECK(group(0, -1));
+ CHECK(remaining(1, -1));
+ CHECK(end());
+
+ setup(2);
+ CHECK(edge(0, 1));
+ run();
+ CHECK(group(0, -1));
+ CHECK(group(1, -1));
+ CHECK(remaining(-1));
+ CHECK(end());
+
+ return true;
+}
+
+unsigned vertex_count;
+TestComponentFinder* finder;
+TestNode* resultsList;
+
+void setup(unsigned count) {
+ vertex_count = count;
+ for (unsigned i = 0; i < MaxVertices; ++i) {
+ TestNode& v = Vertex[i];
+ v.gcGraphEdges.clear();
+ v.gcNextGraphNode = nullptr;
+ v.index = i;
+ }
+}
+
+bool edge(unsigned src_index, unsigned dest_index) {
+ return Vertex[src_index].gcGraphEdges.put(&Vertex[dest_index]);
+}
+
+void run() {
+ finder =
+ new TestComponentFinder(cx->nativeStackLimit[JS::StackForSystemCode]);
+ for (unsigned i = 0; i < vertex_count; ++i) {
+ finder->addNode(&Vertex[i]);
+ }
+ resultsList = finder->getResultsList();
+}
+
+bool group(int vertex, ...) {
+ TestNode* v = resultsList;
+
+ va_list ap;
+ va_start(ap, vertex);
+ while (vertex != -1) {
+ CHECK(v != nullptr);
+ CHECK(v->index == unsigned(vertex));
+ v = v->nextNodeInGroup();
+ vertex = va_arg(ap, int);
+ }
+ va_end(ap);
+
+ CHECK(v == nullptr);
+ resultsList = resultsList->nextGroup();
+ return true;
+}
+
+bool remaining(int vertex, ...) {
+ TestNode* v = resultsList;
+
+ va_list ap;
+ va_start(ap, vertex);
+ while (vertex != -1) {
+ CHECK(v != nullptr);
+ CHECK(v->index == unsigned(vertex));
+ v = (TestNode*)v->gcNextGraphNode;
+ vertex = va_arg(ap, int);
+ }
+ va_end(ap);
+
+ CHECK(v == nullptr);
+ resultsList = nullptr;
+ return true;
+}
+
+bool end() {
+ CHECK(resultsList == nullptr);
+
+ delete finder;
+ finder = nullptr;
+ return true;
+}
+END_TEST(testFindSCCs)
+
+BEGIN_TEST(testFindSCCsStackLimit) {
+ /*
+ * Test what happens if recusion causes the stack to become full while
+ * traversing the graph.
+ *
+ * The test case is a large number of vertices, almost all of which are
+ * arranged in a linear chain. The last few are left unlinked to exercise
+ * adding vertices after the stack full condition has already been detected.
+ *
+ * Such an arrangement with no cycles would normally result in one group for
+ * each vertex, but since the stack is exhasted in processing a single group
+ * is returned containing all the vertices.
+ */
+ const unsigned max = 1000000;
+ const unsigned initial = 10;
+
+ TestNode* vertices = new TestNode[max]();
+ for (unsigned i = initial; i < (max - 10); ++i) {
+ CHECK(vertices[i].gcGraphEdges.put(&vertices[i + 1]));
+ }
+
+ TestComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode]);
+ for (unsigned i = 0; i < max; ++i) {
+ finder.addNode(&vertices[i]);
+ }
+
+ TestNode* r = finder.getResultsList();
+ CHECK(r);
+ TestNode* v = r;
+
+ unsigned count = 0;
+ while (v) {
+ ++count;
+ v = v->nextNodeInGroup();
+ }
+ CHECK(count == max - initial);
+
+ count = 0;
+ v = r->nextGroup();
+ while (v) {
+ ++count;
+ CHECK(!v->nextNodeInGroup());
+ v = v->nextGroup();
+ }
+
+ delete[] vertices;
+ return true;
+}
+END_TEST(testFindSCCsStackLimit)