add unit tests for the different components of the parttion tool

This commit is contained in:
Moritz Kobitzsch
2017-02-02 15:53:42 +01:00
committed by Patrick Niklaus
parent e316dad1cb
commit b789da45bd
25 changed files with 917 additions and 152 deletions
+154
View File
@@ -0,0 +1,154 @@
#include "partition/bisection_graph.hpp"
#include "partition/graph_generator.hpp"
#include <algorithm>
#include <vector>
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
using namespace osrm::partition;
using namespace osrm::util;
BOOST_AUTO_TEST_SUITE(bisection_graph)
BOOST_AUTO_TEST_CASE(access_nodes)
{
// 40 entries of left/right edges
double step_size = 0.01;
int rows = 10;
int cols = 4;
const auto coordinates = makeGridCoordinates(rows, cols, step_size, 0, 0);
std::vector<EdgeWithSomeAdditionalData> grid_edges;
auto graph = makeBisectionGraph(coordinates, adaptToBisectionEdge(std::move(grid_edges)));
const auto to_row = [cols](const NodeID nid) { return nid / cols; };
const auto to_col = [cols](const NodeID nid) { return nid % cols; };
const auto get_expected = [&](const NodeID id) {
const auto expected_lon = FloatLongitude{to_col(id) * step_size};
const auto expected_lat = FloatLatitude{to_row(id) * step_size};
Coordinate compare(expected_lon, expected_lat);
return compare;
};
// check const access
BOOST_CHECK_EQUAL(graph.NumberOfNodes(), 40);
{
NodeID increasing = 0;
for (const auto &node : graph.Nodes())
{
const auto id = graph.GetID(node);
BOOST_CHECK_EQUAL(id, increasing++);
BOOST_CHECK_EQUAL(node.coordinate, get_expected(id));
}
}
// non-const access
{
NodeID increasing = 0;
for (auto &node : graph.Nodes())
{
const auto id = graph.GetID(node);
BOOST_CHECK_EQUAL(id, increasing++);
BOOST_CHECK_EQUAL(node.coordinate, get_expected(id));
}
}
// iterator access
{
const auto begin = graph.Begin();
const auto end = graph.End();
NodeID increasing = 0;
for (auto itr = begin; itr != end; ++itr)
{
const auto id = static_cast<NodeID>(std::distance(begin, itr));
BOOST_CHECK_EQUAL(id, increasing++);
BOOST_CHECK_EQUAL(itr->coordinate, get_expected(id));
}
}
// const iterator access
{
const auto begin = graph.CBegin();
const auto end = graph.CEnd();
NodeID increasing = 0;
for (auto itr = begin; itr != end; ++itr)
{
const auto id = static_cast<NodeID>(std::distance(begin, itr));
BOOST_CHECK_EQUAL(id, increasing++);
BOOST_CHECK_EQUAL(itr->coordinate, get_expected(id));
}
}
}
BOOST_AUTO_TEST_CASE(access_edges)
{
// 40 entries of left/right edges
double step_size = 0.01;
int rows = 10;
int cols = 4;
const auto coordinates = makeGridCoordinates(rows, cols, step_size, 0, 0);
auto grid_edges = makeGridEdges(rows, cols, 0);
std::random_shuffle(grid_edges.begin(), grid_edges.end());
groupEdgesBySource(grid_edges.begin(), grid_edges.end());
const auto graph = makeBisectionGraph(coordinates, adaptToBisectionEdge(std::move(grid_edges)));
const auto to_row = [cols](const NodeID nid) { return nid / cols; };
const auto to_col = [cols](const NodeID nid) { return nid % cols; };
BOOST_CHECK_EQUAL(graph.NumberOfNodes(), 40);
for (const auto &node : graph.Nodes())
{
for (const auto &edge : graph.Edges(node))
{
BOOST_CHECK(edge.target < graph.NumberOfNodes());
BOOST_CHECK(std::abs(static_cast<int>(to_row(graph.GetID(node))) -
static_cast<int>(to_row(edge.target))) <= 1);
BOOST_CHECK(std::abs(static_cast<int>(to_col(graph.GetID(node))) -
static_cast<int>(to_col(edge.target))) <= 1);
}
// itr of node
for (auto itr = graph.BeginEdges(node); itr != graph.EndEdges(node); ++itr)
{
BOOST_CHECK(itr->target < graph.NumberOfNodes());
BOOST_CHECK(std::abs(static_cast<int>(to_row(graph.GetID(node))) -
static_cast<int>(to_row(itr->target))) <= 1);
BOOST_CHECK(std::abs(static_cast<int>(to_col(graph.GetID(node))) -
static_cast<int>(to_col(itr->target))) <= 1);
}
// access via ID of ndoe
const auto id = graph.GetID(node);
for (const auto &edge : graph.Edges(id))
{
BOOST_CHECK(edge.target < graph.NumberOfNodes());
BOOST_CHECK(std::abs(static_cast<int>(to_row(graph.GetID(node))) -
static_cast<int>(to_row(edge.target))) <= 1);
BOOST_CHECK(std::abs(static_cast<int>(to_col(graph.GetID(node))) -
static_cast<int>(to_col(edge.target))) <= 1);
}
for (auto itr = graph.BeginEdges(id); itr != graph.EndEdges(id); ++itr)
{
BOOST_CHECK(itr->target < graph.NumberOfNodes());
BOOST_CHECK(std::abs(static_cast<int>(to_row(graph.GetID(node))) -
static_cast<int>(to_row(itr->target))) <= 1);
BOOST_CHECK(std::abs(static_cast<int>(to_col(graph.GetID(node))) -
static_cast<int>(to_col(itr->target))) <= 1);
}
for (auto eid = graph.BeginEdgeID(id); eid != graph.EndEdgeID(id); ++eid)
{
const auto &itr = graph.Edge(eid);
BOOST_CHECK(itr.target < graph.NumberOfNodes());
BOOST_CHECK(std::abs(static_cast<int>(to_row(graph.GetID(node))) -
static_cast<int>(to_row(itr.target))) <= 1);
BOOST_CHECK(std::abs(static_cast<int>(to_col(graph.GetID(node))) -
static_cast<int>(to_col(itr.target))) <= 1);
}
}
}
BOOST_AUTO_TEST_SUITE_END()
+75
View File
@@ -0,0 +1,75 @@
#include "partition/dinic_max_flow.hpp"
#include "partition/graph_generator.hpp"
#include "partition/graph_view.hpp"
#include "partition/recursive_bisection_state.hpp"
#include <algorithm>
#include <vector>
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
using namespace osrm::partition;
using namespace osrm::util;
BOOST_AUTO_TEST_SUITE(dinic_algorithm)
BOOST_AUTO_TEST_CASE(horizontal_cut_between_two_grids)
{
// 40 entries of left/right edges
const double step_size = 0.01;
const int rows = 10;
const int cols = 10;
// build a small grid (10*10) and a (100 * 10) below (to make the different steps unique)
auto graph = [&]() {
std::vector<Coordinate> grid_coordinates;
std::vector<EdgeWithSomeAdditionalData> grid_edges;
const auto connect = [&grid_edges](const NodeID from, const NodeID to) {
grid_edges.push_back({from, to, 1});
grid_edges.push_back({to, from, 1});
};
// 10 rows of large components, interrupted by small disconnected components
const auto small_coordinates = makeGridCoordinates(rows, cols, step_size, 0, 0);
grid_coordinates.insert(
grid_coordinates.end(), small_coordinates.begin(), small_coordinates.end());
// connect the grid edges, starting with i * (rows * cols + 1) as first id (0,11,22...)
const auto small_edges = makeGridEdges(rows, cols, 0);
grid_edges.insert(grid_edges.end(), small_edges.begin(), small_edges.end());
const auto large_coordinates =
makeGridCoordinates(10 * rows, cols, step_size, 0, rows * step_size);
grid_coordinates.insert(
grid_coordinates.end(), large_coordinates.begin(), large_coordinates.end());
const auto large_edges = makeGridEdges(10 * rows, cols, (rows * cols));
grid_edges.insert(grid_edges.end(), large_edges.begin(), large_edges.end());
connect(45, 1001);
connect(55, 800);
connect(65, 600);
connect(75, 200);
groupEdgesBySource(grid_edges.begin(), grid_edges.end());
return makeBisectionGraph(grid_coordinates, adaptToBisectionEdge(std::move(grid_edges)));
}();
RecursiveBisectionState bisection_state(graph);
GraphView view(graph);
DinicMaxFlow::SourceSinkNodes sources, sinks;
for (int i = 0; i < 10; ++i)
{
sources.insert(static_cast<NodeID>(i));
sinks.insert(static_cast<NodeID>(1000 + i));
}
DinicMaxFlow flow;
const auto cut = flow(view, sources, sinks);
BOOST_CHECK(cut.num_edges == 4);
}
BOOST_AUTO_TEST_SUITE_END()
+80
View File
@@ -0,0 +1,80 @@
#ifndef OSRM_UNIT_TEST_PARTITION_GRAPH_GENERATOR_HPP
#define OSRM_UNIT_TEST_PARTITION_GRAPH_GENERATOR_HPP
#include "util/coordinate.hpp"
#include "util/typedefs.hpp"
#include <algorithm>
#include <vector>
using namespace osrm::util;
struct EdgeWithSomeAdditionalData
{
NodeID source;
NodeID target;
unsigned important_data;
};
inline Coordinate
makeCoordinate(int x, int y, double step_size, double offset_x = 0, double offset_y = 0)
{
return {FloatLongitude{offset_x + x * step_size}, FloatLatitude{offset_y + y * step_size}};
}
std::vector<Coordinate> inline makeGridCoordinates(
int rows, int columns, double step_size, double lon_base, double lat_base)
{
std::vector<Coordinate> result;
for (int r = 0; r < rows; ++r)
for (int c = 0; c < columns; ++c)
result.push_back(makeCoordinate(c, r, step_size, lon_base, lat_base));
return result;
}
inline std::vector<EdgeWithSomeAdditionalData> makeGridEdges(int rows, int columns, int id_base)
{
const int min_id = id_base;
const int max_id = id_base + rows * columns;
const auto get_id = [id_base, columns](int r, int c) { return id_base + r * columns + c; };
const auto valid = [min_id, max_id](int id) { return id >= min_id && id < max_id; };
std::vector<EdgeWithSomeAdditionalData> edges;
for (int r = 0; r < rows; ++r)
{
for (int c = 0; c < columns; ++c)
{
auto id = static_cast<NodeID>(get_id(r, c));
if (c > 0)
{
auto left = get_id(r, c - 1);
edges.push_back({id, static_cast<NodeID>(left), 1});
}
if (c + 1 < columns)
{
auto right = get_id(r, c + 1);
if (valid(right))
edges.push_back({id, static_cast<NodeID>(right), 1});
}
if (r > 0)
{
auto top = get_id(r - 1, c);
if (valid(top))
edges.push_back({id, static_cast<NodeID>(top), 1});
}
if (r + 1 < rows)
{
auto bottom = get_id(r + 1, c);
if (valid(bottom))
edges.push_back({id, static_cast<NodeID>(bottom), 1});
}
}
}
return edges;
}
#endif // OSRM_UNIT_TEST_PARTITION_GRAPH_GENERATOR_HPP
+175
View File
@@ -0,0 +1,175 @@
#include "partition/graph_view.hpp"
#include "partition/graph_generator.hpp"
#include "partition/recursive_bisection_state.hpp"
#include <algorithm>
#include <vector>
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
using namespace osrm::partition;
using namespace osrm::util;
BOOST_AUTO_TEST_SUITE(graph_view)
BOOST_AUTO_TEST_CASE(separate_top_bottom)
{
// 40 entries of left/right edges
double step_size = 0.01;
int rows = 2;
int cols = 4;
const auto coordinates = makeGridCoordinates(rows, cols, step_size, 0, 0);
auto grid_edges = makeGridEdges(rows, cols, 0);
std::random_shuffle(grid_edges.begin(), grid_edges.end());
groupEdgesBySource(grid_edges.begin(), grid_edges.end());
auto graph = makeBisectionGraph(coordinates, adaptToBisectionEdge(std::move(grid_edges)));
RecursiveBisectionState bisection_state(graph);
std::vector<bool> partition(8, false);
partition[4] = partition[5] = partition[6] = partition[7] = true;
const auto center = bisection_state.ApplyBisection(graph.Begin(), graph.End(), 0, partition);
GraphView left(graph, graph.Begin(), center);
GraphView right(graph, center, graph.End());
BOOST_CHECK_EQUAL(left.NumberOfNodes(), 4);
for (const auto &node : left.Nodes())
{
auto id = left.GetID(node);
const auto compare = makeCoordinate(id, 0, step_size);
BOOST_CHECK_EQUAL(compare, node.coordinate);
BOOST_CHECK(id < left.NumberOfNodes());
BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 0);
for (const auto &edge : left.Edges(node))
BOOST_CHECK(edge.target < left.NumberOfNodes());
}
BOOST_CHECK_EQUAL(right.NumberOfNodes(), 4);
for (const auto &node : right.Nodes())
{
BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 1);
auto id = right.GetID(node);
const auto compare = makeCoordinate(id, 1, step_size);
BOOST_CHECK_EQUAL(compare, node.coordinate);
BOOST_CHECK(id < right.NumberOfNodes());
for (const auto &edge : right.Edges(node))
BOOST_CHECK(edge.target < right.NumberOfNodes());
}
}
BOOST_AUTO_TEST_CASE(separate_top_bottom_copy)
{
// 40 entries of left/right edges
double step_size = 0.01;
int rows = 2;
int cols = 4;
const auto coordinates = makeGridCoordinates(rows, cols, step_size, 0, 0);
auto grid_edges = makeGridEdges(rows, cols, 0);
std::random_shuffle(grid_edges.begin(), grid_edges.end());
groupEdgesBySource(grid_edges.begin(), grid_edges.end());
auto graph = makeBisectionGraph(coordinates, adaptToBisectionEdge(std::move(grid_edges)));
RecursiveBisectionState bisection_state(graph);
std::vector<bool> partition(8, false);
partition[4] = partition[5] = partition[6] = partition[7] = true;
const auto center = bisection_state.ApplyBisection(graph.Begin(), graph.End(), 0, partition);
GraphView total(graph, graph.Begin(), graph.End());
GraphView left(total, total.Begin(), center);
GraphView right(total, center, total.End());
BOOST_CHECK_EQUAL(left.NumberOfNodes(), 4);
for (const auto &node : left.Nodes())
{
auto id = left.GetID(node);
const auto compare = makeCoordinate(id, 0, step_size);
BOOST_CHECK_EQUAL(compare, node.coordinate);
BOOST_CHECK(id < left.NumberOfNodes());
BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 0);
for( const auto & edge : left.Edges(id) )
BOOST_CHECK(edge.target < left.NumberOfNodes());
}
BOOST_CHECK_EQUAL(right.NumberOfNodes(), 4);
for( NodeID id = 0; id < right.NumberOfNodes(); ++id )
{
const auto &node = right.Node(id);
BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 1);
const auto compare = makeCoordinate(id, 1, step_size);
BOOST_CHECK_EQUAL(compare, node.coordinate);
BOOST_CHECK(id < right.NumberOfNodes());
for (const auto &edge : right.Edges(id))
BOOST_CHECK(edge.target < right.NumberOfNodes());
}
}
BOOST_AUTO_TEST_CASE(separate_left_right)
{
// 40 entries of left/right edges
double step_size = 0.01;
int rows = 2;
int cols = 4;
const auto coordinates = makeGridCoordinates(rows, cols, step_size, 0, 0);
auto grid_edges = makeGridEdges(rows, cols, 0);
std::random_shuffle(grid_edges.begin(), grid_edges.end());
groupEdgesBySource(grid_edges.begin(), grid_edges.end());
auto graph = makeBisectionGraph(coordinates, adaptToBisectionEdge(std::move(grid_edges)));
// separate Left 2 nodes from rest
RecursiveBisectionState bisection_state(graph);
std::vector<bool> partition(8, true);
partition[0] = partition[4] = false;
const auto center = bisection_state.ApplyBisection(graph.Begin(), graph.End(), 0, partition);
GraphView left(graph, graph.Begin(), center);
GraphView right(graph, center, graph.End());
BOOST_CHECK_EQUAL(left.NumberOfNodes(), 2);
std::vector<Coordinate> left_coordinates;
left_coordinates.push_back(makeCoordinate(0, 0, step_size));
left_coordinates.push_back(makeCoordinate(0, 1, step_size));
auto left_compare = left_coordinates.begin();
for (const auto &node : left.Nodes())
{
auto id = left.GetID(node);
const auto compare = *left_compare++;
BOOST_CHECK_EQUAL(compare, node.coordinate);
BOOST_CHECK(id < left.NumberOfNodes());
BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 0);
for (const auto &edge : left.Edges(node))
BOOST_CHECK(edge.target < left.NumberOfNodes());
}
BOOST_CHECK_EQUAL(right.NumberOfNodes(), 6);
std::vector<Coordinate> right_coordinates;
right_coordinates.push_back(makeCoordinate(1, 0, step_size));
right_coordinates.push_back(makeCoordinate(2, 0, step_size));
right_coordinates.push_back(makeCoordinate(3, 0, step_size));
right_coordinates.push_back(makeCoordinate(1, 1, step_size));
right_coordinates.push_back(makeCoordinate(2, 1, step_size));
right_coordinates.push_back(makeCoordinate(3, 1, step_size));
auto right_compare = right_coordinates.begin();
for (const auto &node : right.Nodes())
{
BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 1);
auto id = right.GetID(node);
const auto compare = *right_compare++;
BOOST_CHECK_EQUAL(compare, node.coordinate);
BOOST_CHECK(id < right.NumberOfNodes());
for (const auto &edge : right.Edges(node))
BOOST_CHECK(edge.target < right.NumberOfNodes());
}
}
BOOST_AUTO_TEST_SUITE_END()
@@ -0,0 +1,79 @@
#include "partition/recursive_bisection.hpp"
#include "partition/graph_generator.hpp"
#include "partition/recursive_bisection_state.hpp"
#include <algorithm>
#include <vector>
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
using namespace osrm::partition;
using namespace osrm::util;
BOOST_AUTO_TEST_SUITE(recursive_bisection)
BOOST_AUTO_TEST_CASE(dividing_four_grid_cells)
{
// 40 entries of left/right edges
const double step_size = 0.01;
const int rows = 10;
const int cols = 10;
const int cut_edges = 4;
auto graph = [&]() {
std::vector<Coordinate> grid_coordinates;
std::vector<EdgeWithSomeAdditionalData> grid_edges;
const auto connect =
[&grid_edges](int min_left, int max_left, int min_right, int max_right) {
const NodeID source = (rand() % (max_left - min_left)) + min_left;
const NodeID target = (rand() % (max_right - min_right)) + min_right;
grid_edges.push_back({source, target, 1});
grid_edges.push_back({target, source, 1});
};
// generate 10 big components
for (int i = 0; i < 4; ++i)
{
// 10 rows of large components, interrupted by small disconnected components
const auto coordinates = makeGridCoordinates(
rows, cols, step_size, cols * (i % 2), (i * rows / 2) * step_size);
grid_coordinates.insert(grid_coordinates.end(), coordinates.begin(), coordinates.end());
// connect the grid edges, starting with i * (rows * cols + 1) as first id (0,11,22...)
const auto edges = makeGridEdges(rows, cols, i * (rows * cols));
grid_edges.insert(grid_edges.end(), edges.begin(), edges.end());
}
// add cut edges between neighboring cells
int n = rows * cols;
for (int i = 0; i < cut_edges; ++i)
{
// left/right
connect(0, n, n, 2 * n);
connect(2 * n, 3 * n, 3 * n, 4 * n);
// top/bottom
connect(0, n, 2 * n, 3 * n);
connect(n, 2 * n, 3 * n, 4 * n);
}
groupEdgesBySource(grid_edges.begin(), grid_edges.end());
return makeBisectionGraph(grid_coordinates, adaptToBisectionEdge(std::move(grid_edges)));
}();
RecursiveBisection bisection(graph, 120, 1.1, 0.25, 10, 1);
const auto result = bisection.BisectionIDs();
// all same IDs withing a group
for (int i = 0; i < 4; ++i)
for (int j = 0; j < rows * cols; ++j)
BOOST_CHECK(result[i * (rows * cols)] == result[i * (rows * cols) + j]);
// different IDs for all four groups
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
BOOST_CHECK(i == j || result[i * (rows * cols)] != result[j * (rows * cols)]);
}
BOOST_AUTO_TEST_SUITE_END()
+2 -2
View File
@@ -35,10 +35,10 @@ BOOST_AUTO_TEST_CASE(reordering_n_shuffles_n_smallest_to_front_n_largest_to_back
reorderFirstLast(begin(range), end(range), 2, std::less<>{});
// Smallest at front, but: no ordering guarantee in that subrange!
BOOST_CHECK((range[0] == 2 and range[1] == 3) or (range[0] == 3 and range[1] == 2));
BOOST_CHECK((range[0] == 2 && range[1] == 3) || (range[0] == 3 && range[1] == 2));
// Largest at back, but: no ordering guarantee in that subrange!
BOOST_CHECK((range[2] == 8 and range[3] == 9) or (range[2] == 9 and range[3] == 8));
BOOST_CHECK((range[2] == 8 && range[3] == 9) || (range[2] == 9 && range[3] == 8));
}
BOOST_AUTO_TEST_CASE(reordering_n_with_iterators)
+80
View File
@@ -0,0 +1,80 @@
#include "partition/graph_generator.hpp"
#include "partition/graph_view.hpp"
#include "partition/recursive_bisection_state.hpp"
#include <algorithm>
#include <vector>
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
using namespace osrm::partition;
using namespace osrm::util;
BOOST_AUTO_TEST_SUITE(scc_integration)
BOOST_AUTO_TEST_CASE(graph_views_on_components)
{
// 40 entries of left/right edges
const double step_size = 0.01;
const int rows = 1;
const int cols = 10;
const int num_components = 10;
auto graph = [&]() {
std::vector<Coordinate> grid_coordinates;
std::vector<EdgeWithSomeAdditionalData> grid_edges;
// generate 10 big components
for (int i = 0; i < num_components; ++i)
{
// 10 rows of large components, interrupted by small disconnected components
const auto coordinates = makeGridCoordinates(rows, cols, step_size, 0, i * step_size);
grid_coordinates.insert(grid_coordinates.end(), coordinates.begin(), coordinates.end());
// add a single disconnected node to have ids between large components
if (i + 1 < num_components)
grid_coordinates.push_back(
makeCoordinate(1, 1, 0.5 * step_size, 0, (i + 1) * step_size));
// connect the grid edges, starting with i * (rows * cols + 1) as first id (0,11,22...)
const auto edges = makeGridEdges(rows, cols, i * (rows * cols + 1));
grid_edges.insert(grid_edges.end(), edges.begin(), edges.end());
}
groupEdgesBySource(grid_edges.begin(), grid_edges.end());
return makeBisectionGraph(grid_coordinates, adaptToBisectionEdge(std::move(grid_edges)));
}();
RecursiveBisectionState bisection_state(graph);
auto views = bisection_state.PrePartitionWithSCC(2);
BOOST_CHECK_EQUAL(views.size(), num_components + 1); // big components + 1 small one
for (std::size_t comp = 0; comp < num_components; ++comp)
{
const auto &view = views[comp];
BOOST_CHECK(views[comp].NumberOfNodes() == 10);
const auto to_component_id = [&](const auto &node) {
return node.original_id / (rows * cols + 1);
};
std::size_t expected_component_id = to_component_id(view.Node(0));
BOOST_CHECK(std::all_of(view.Begin(), view.End(), [&](const auto &node) {
return to_component_id(node) == expected_component_id;
}));
for (const auto &node : view.Nodes())
{
for (const auto &edge : view.Edges(node))
{
BOOST_CHECK(edge.target < view.NumberOfNodes());
}
}
}
BOOST_CHECK(views.back().NumberOfNodes() == 9);
}
BOOST_AUTO_TEST_SUITE_END()