blob: ad38b56ac036af5091db48e741797db46d6dbb42 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*/
/* 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 "tests.h"
#include <string.h>
#include <stdarg.h>
#include "jscntxt.h"
#include "jsgc.h"
#include "gc/FindSCCs.h"
#include "gc/FindSCCs-inl.h"
static const unsigned MaxVertices = 10;
using js::gc::GraphNodeBase;
using js::gc::ComponentFinder;
struct TestNode : public GraphNodeBase<TestNode>
{
unsigned index;
bool hasEdge[MaxVertices];
void findOutgoingEdges(ComponentFinder<TestNode> &finder);
};
static TestNode Vertex[MaxVertices];
void
TestNode::findOutgoingEdges(ComponentFinder<TestNode> &finder)
{
for (unsigned i = 0; i < MaxVertices; ++i) {
if (hasEdge[i])
finder.addEdgeTo(&Vertex[i]);
}
}
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);
edge(0, 1);
edge(1, 2);
run();
CHECK(group(0, -1));
CHECK(group(1, -1));
CHECK(group(2, -1));
CHECK(end());
// tree
setup(3);
edge(0, 1);
edge(0, 2);
run();
CHECK(group(0, -1));
CHECK(group(2, -1));
CHECK(group(1, -1));
CHECK(end());
// cycles
setup(3);
edge(0, 1);
edge(1, 2);
edge(2, 0);
run();
CHECK(group(0, 1, 2, -1));
CHECK(end());
setup(4);
edge(0, 1);
edge(1, 2);
edge(2, 1);
edge(2, 3);
run();
CHECK(group(0, -1));
CHECK(group(1, 2, -1));
CHECK(group(3, -1));
CHECK(end());
// remaining
setup(2);
edge(0, 1);
run();
CHECK(remaining(0, 1, -1));
CHECK(end());
setup(2);
edge(0, 1);
run();
CHECK(group(0, -1));
CHECK(remaining(1, -1));
CHECK(end());
setup(2);
edge(0, 1);
run();
CHECK(group(0, -1));
CHECK(group(1, -1));
CHECK(remaining(-1));
CHECK(end());
return true;
}
unsigned vertex_count;
ComponentFinder<TestNode> *finder;
TestNode *resultsList;
void setup(unsigned count)
{
vertex_count = count;
for (unsigned i = 0; i < MaxVertices; ++i) {
TestNode &v = Vertex[i];
v.gcNextGraphNode = NULL;
v.index = i;
memset(&v.hasEdge, 0, sizeof(v.hasEdge));
}
}
void edge(unsigned src_index, unsigned dest_index)
{
Vertex[src_index].hasEdge[dest_index] = true;
}
void run()
{
finder = new ComponentFinder<TestNode>(rt->mainThread.nativeStackLimit);
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 != NULL);
CHECK(v->index == unsigned(vertex));
v = v->nextNodeInGroup();
vertex = va_arg(ap, int);
}
va_end(ap);
CHECK(v == NULL);
resultsList = resultsList->nextGroup();
return true;
}
bool remaining(int vertex, ...)
{
TestNode *v = resultsList;
va_list ap;
va_start(ap, vertex);
while (vertex != -1) {
CHECK(v != NULL);
CHECK(v->index == unsigned(vertex));
v = (TestNode *)v->gcNextGraphNode;
vertex = va_arg(ap, int);
}
va_end(ap);
CHECK(v == NULL);
resultsList = NULL;
return true;
}
bool end()
{
CHECK(resultsList == NULL);
delete finder;
finder = NULL;
return true;
}
END_TEST(testFindSCCs)
struct TestNode2 : public GraphNodeBase<TestNode2>
{
TestNode2 *edge;
TestNode2() :
edge(NULL)
{
}
void
findOutgoingEdges(ComponentFinder<TestNode2> &finder) {
if (edge)
finder.addEdgeTo(edge);
}
};
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;
TestNode2 *vertices = new TestNode2[max]();
for (unsigned i = initial; i < (max - 10); ++i)
vertices[i].edge = &vertices[i + 1];
ComponentFinder<TestNode2> finder(rt->mainThread.nativeStackLimit);
for (unsigned i = 0; i < max; ++i)
finder.addNode(&vertices[i]);
TestNode2 *r = finder.getResultsList();
CHECK(r);
TestNode2 *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)