[Groonga-commit] groonga/grnxx at 4d1f776 [master] Divide tests into separate source files.

Back to archive index

susumu.yata null+****@clear*****
Mon Aug 18 11:30:17 JST 2014


susumu.yata	2014-08-18 11:30:17 +0900 (Mon, 18 Aug 2014)

  New Revision: 4d1f77698c8e9bbd9f2f3cef2408cc047a5ede42
  https://github.com/groonga/grnxx/commit/4d1f77698c8e9bbd9f2f3cef2408cc047a5ede42

  Message:
    Divide tests into separate source files.

  Added files:
    test/test_column.cpp
    test/test_db.cpp
    test/test_expression.cpp
    test/test_sorter.cpp
    test/test_table.cpp
  Removed files:
    test/test_grnxx.cpp
  Modified files:
    .gitignore
    test/Makefile.am

  Modified: .gitignore (+5 -1)
===================================================================
--- .gitignore    2014-08-12 19:05:59 +0900 (7454c89)
+++ .gitignore    2014-08-18 11:30:17 +0900 (67866cf)
@@ -29,6 +29,10 @@ oprofile_data/
 src/grnxx
 stamp-h1
 test/*.trs
-test/test_grnxx
+test/test_db
+test/test_table
+test/test_column
+test/test_expression
+test/test_sorter
 test/benchmark_grnxx
 test-driver

  Modified: test/Makefile.am (+19 -3)
===================================================================
--- test/Makefile.am    2014-08-12 19:05:59 +0900 (beb7883)
+++ test/Makefile.am    2014-08-18 11:30:17 +0900 (526cb32)
@@ -1,11 +1,27 @@
 TESTS =					\
-	test_grnxx			\
+	test_db				\
+	test_table			\
+	test_column			\
+	test_expression			\
+	test_sorter			\
 	benchmark_grnxx
 
 check_PROGRAMS = $(TESTS)
 
-test_grnxx_SOURCES = test_grnxx.cpp
-test_grnxx_LDADD = $(top_srcdir)/lib/grnxx/libgrnxx.la
+test_db_SOURCES = test_db.cpp
+test_db_LDADD = $(top_srcdir)/lib/grnxx/libgrnxx.la
+
+test_table_SOURCES = test_table.cpp
+test_table_LDADD = $(top_srcdir)/lib/grnxx/libgrnxx.la
+
+test_column_SOURCES = test_column.cpp
+test_column_LDADD = $(top_srcdir)/lib/grnxx/libgrnxx.la
+
+test_expression_SOURCES = test_expression.cpp
+test_expression_LDADD = $(top_srcdir)/lib/grnxx/libgrnxx.la
+
+test_sorter_SOURCES = test_sorter.cpp
+test_sorter_LDADD = $(top_srcdir)/lib/grnxx/libgrnxx.la
 
 benchmark_grnxx_SOURCES = benchmark_grnxx.cpp
 benchmark_grnxx_LDADD = $(top_srcdir)/lib/grnxx/libgrnxx.la

  Added: test/test_column.cpp (+133 -0) 100644
===================================================================
--- /dev/null
+++ test/test_column.cpp    2014-08-18 11:30:17 +0900 (4e59ff7)
@@ -0,0 +1,133 @@
+/*
+  Copyright (C) 2012-2014  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <cassert>
+#include <iostream>
+
+#include "grnxx/column.hpp"
+#include "grnxx/cursor.hpp"
+#include "grnxx/datum.hpp"
+#include "grnxx/db.hpp"
+#include "grnxx/error.hpp"
+#include "grnxx/table.hpp"
+
+void test_column() {
+  grnxx::Error error;
+
+  // Create a database with the default options.
+  auto db = grnxx::open_db(&error, "");
+  assert(db);
+
+  // Create a table with the default options.
+  auto table = db->create_table(&error, "Table");
+  assert(table);
+
+  // Append the first row.
+  grnxx::Int row_id;
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+
+  // Create a column named "BoolColumn".
+  // The column stores Bool values.
+  auto bool_column = table->create_column(&error, "BoolColumn",
+                                          grnxx::BOOL_DATA);
+  assert(bool_column);
+  assert(bool_column->table() == table);
+  assert(bool_column->name() == "BoolColumn");
+  assert(bool_column->data_type() == grnxx::BOOL_DATA);
+  assert(!bool_column->has_key_attribute());
+  assert(bool_column->num_indexes() == 0);
+
+  // Create a column named "IntColumn".
+  // The column stores Int values.
+  auto int_column = table->create_column(&error, "IntColumn",
+                                         grnxx::INT_DATA);
+  assert(int_column);
+  assert(int_column->table() == table);
+  assert(int_column->name() == "IntColumn");
+  assert(int_column->data_type() == grnxx::INT_DATA);
+  assert(!int_column->has_key_attribute());
+  assert(int_column->num_indexes() == 0);
+
+  // Create a column named "FloatColumn".
+  // The column stores Float values.
+  auto float_column = table->create_column(&error, "FloatColumn",
+                                           grnxx::FLOAT_DATA);
+  assert(float_column);
+  assert(float_column->table() == table);
+  assert(float_column->name() == "FloatColumn");
+  assert(float_column->data_type() == grnxx::FLOAT_DATA);
+  assert(!float_column->has_key_attribute());
+  assert(float_column->num_indexes() == 0);
+
+  // Create a column named "TextColumn".
+  // The column stores Text values.
+  auto text_column = table->create_column(&error, "TextColumn",
+                                           grnxx::TEXT_DATA);
+  assert(text_column);
+  assert(text_column->table() == table);
+  assert(text_column->name() == "TextColumn");
+  assert(text_column->data_type() == grnxx::TEXT_DATA);
+  assert(!text_column->has_key_attribute());
+  assert(text_column->num_indexes() == 0);
+
+  grnxx::Datum datum;
+
+  // Check that the default values are stored.
+  assert(bool_column->get(&error, 1, &datum));
+  assert(datum.type() == grnxx::BOOL_DATA);
+  assert(!datum.force_bool());
+
+  assert(int_column->get(&error, 1, &datum));
+  assert(datum.type() == grnxx::INT_DATA);
+  assert(datum.force_int() == 0);
+
+  assert(float_column->get(&error, 1, &datum));
+  assert(datum.type() == grnxx::FLOAT_DATA);
+  assert(datum.force_float() == 0.0);
+
+  assert(text_column->get(&error, 1, &datum));
+  assert(datum.type() == grnxx::TEXT_DATA);
+  assert(datum.force_text() == "");
+
+  // Set and get values.
+  assert(bool_column->set(&error, 1, grnxx::Bool(true)));
+  assert(int_column->set(&error, 1, grnxx::Int(123)));
+  assert(float_column->set(&error, 1, grnxx::Float(0.25)));
+  assert(text_column->set(&error, 1, grnxx::Text("Hello, world!")));
+
+  assert(bool_column->get(&error, 1, &datum));
+  assert(datum.type() == grnxx::BOOL_DATA);
+  assert(datum.force_bool());
+
+  assert(int_column->get(&error, 1, &datum));
+  assert(datum.type() == grnxx::INT_DATA);
+  assert(datum.force_int() == 123);
+
+  assert(float_column->get(&error, 1, &datum));
+  assert(datum.type() == grnxx::FLOAT_DATA);
+  assert(datum.force_float() == 0.25);
+
+  assert(text_column->get(&error, 1, &datum));
+  assert(datum.type() == grnxx::TEXT_DATA);
+  assert(datum.force_text() == "Hello, world!");
+}
+
+int main() {
+  test_column();
+  return 0;
+}

  Added: test/test_db.cpp (+82 -0) 100644
===================================================================
--- /dev/null
+++ test/test_db.cpp    2014-08-18 11:30:17 +0900 (1234d1f)
@@ -0,0 +1,82 @@
+/*
+  Copyright (C) 2012-2014  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <cassert>
+#include <iostream>
+
+#include "grnxx/db.hpp"
+#include "grnxx/error.hpp"
+#include "grnxx/table.hpp"
+
+void test_db() {
+  grnxx::Error error;
+
+  // Create a database with the default options.
+  auto db = grnxx::open_db(&error, "");
+  assert(db);
+  assert(db->num_tables() == 0);
+
+  // Create a table named "Table_1".
+  auto table = db->create_table(&error, "Table_1");
+  assert(table);
+  assert(table->name() == "Table_1");
+  assert(db->num_tables() == 1);
+
+  assert(db->get_table(0) == table);
+  assert(db->find_table(&error, "Table_1") == table);
+
+  // The following create_table() must fail because "Table_1" already exists.
+  assert(!db->create_table(&error, "Table_1"));
+
+  // Create tables named "Table_2" and "Table_3".
+  assert(db->create_table(&error, "Table_2"));
+  assert(db->create_table(&error, "Table_3"));
+  assert(db->num_tables() == 3);
+
+  // Remove "Table_2".
+  assert(db->remove_table(&error, "Table_2"));
+  assert(db->num_tables() == 2);
+
+  assert(db->get_table(0)->name() == "Table_1");
+  assert(db->get_table(1)->name() == "Table_3");
+
+  // Recreate "Table_2".
+  assert(db->create_table(&error, "Table_2"));
+
+  // Move "Table_3" to the next to "Table_2".
+  assert(db->reorder_table(&error, "Table_3", "Table_2"));
+  assert(db->get_table(0)->name() == "Table_1");
+  assert(db->get_table(1)->name() == "Table_2");
+  assert(db->get_table(2)->name() == "Table_3");
+
+  // Move "Table_3" to the head.
+  assert(db->reorder_table(&error, "Table_3", ""));
+  assert(db->get_table(0)->name() == "Table_3");
+  assert(db->get_table(1)->name() == "Table_1");
+  assert(db->get_table(2)->name() == "Table_2");
+
+  // Move "Table_2" to the next to "Table_3".
+  assert(db->reorder_table(&error, "Table_2", "Table_3"));
+  assert(db->get_table(0)->name() == "Table_3");
+  assert(db->get_table(1)->name() == "Table_2");
+  assert(db->get_table(2)->name() == "Table_1");
+}
+
+int main() {
+  test_db();
+  return 0;
+}

  Added: test/test_expression.cpp (+461 -0) 100644
===================================================================
--- /dev/null
+++ test/test_expression.cpp    2014-08-18 11:30:17 +0900 (ef0b63f)
@@ -0,0 +1,461 @@
+/*
+  Copyright (C) 2012-2014  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <cassert>
+#include <iostream>
+
+#include "grnxx/column.hpp"
+#include "grnxx/cursor.hpp"
+#include "grnxx/datum.hpp"
+#include "grnxx/db.hpp"
+#include "grnxx/error.hpp"
+#include "grnxx/expression.hpp"
+#include "grnxx/table.hpp"
+
+void test_expression() {
+  grnxx::Error error;
+
+  // Create a database with the default options.
+  auto db = grnxx::open_db(&error, "");
+  assert(db);
+
+  // Create a table with the default options.
+  auto table = db->create_table(&error, "Table");
+  assert(table);
+
+  // Create columns for Bool, Int, Float, and Text values.
+  auto bool_column = table->create_column(&error, "BoolColumn",
+                                          grnxx::BOOL_DATA);
+  assert(bool_column);
+  auto int_column = table->create_column(&error, "IntColumn",
+                                         grnxx::INT_DATA);
+  assert(int_column);
+  auto float_column = table->create_column(&error, "FloatColumn",
+                                           grnxx::FLOAT_DATA);
+  assert(float_column);
+  auto text_column = table->create_column(&error, "TextColumn",
+                                           grnxx::TEXT_DATA);
+  assert(text_column);
+
+  // Store the following data.
+  //
+  // RowID BoolColumn IntColumn FloatColumn TextColumn
+  //     1      false       123       -0.25      "ABC"
+  //     2       true       456        0.25      "XYZ"
+  grnxx::Int row_id;
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(bool_column->set(&error, row_id, grnxx::Bool(false)));
+  assert(int_column->set(&error, row_id, grnxx::Int(123)));
+  assert(float_column->set(&error, row_id, grnxx::Float(-0.25)));
+  assert(text_column->set(&error, row_id, grnxx::Text("ABC")));
+
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(bool_column->set(&error, row_id, grnxx::Bool(true)));
+  assert(int_column->set(&error, row_id, grnxx::Int(456)));
+  assert(float_column->set(&error, row_id, grnxx::Float(0.25)));
+  assert(text_column->set(&error, row_id, grnxx::Text("XYZ")));
+
+  // Create an object for building expressions.
+  auto builder = grnxx::ExpressionBuilder::create(&error, table);
+  assert(builder);
+
+  // Test an expression (true).
+  assert(builder->push_datum(&error, grnxx::Bool(true)));
+  auto expression = builder->release(&error);
+  assert(expression);
+
+  grnxx::Array<grnxx::Record> records;
+  auto cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 2);
+
+  // Test an expression (100 == 100).
+  assert(builder->push_datum(&error, grnxx::Int(100)));
+  assert(builder->push_datum(&error, grnxx::Int(100)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 2);
+
+  // Test an expression (BoolColumn).
+  assert(builder->push_column(&error, "BoolColumn"));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expression (IntColumn == 123).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(123)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  // Test an expression (IntColumn != 123).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(123)));
+  assert(builder->push_operator(&error, grnxx::NOT_EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison (IntColumn < 300).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(300)));
+  assert(builder->push_operator(&error, grnxx::LESS_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  // Test an expresison (TextColumn <= "ABC").
+  assert(builder->push_column(&error, "TextColumn"));
+  assert(builder->push_datum(&error, grnxx::Text("ABC")));
+  assert(builder->push_operator(&error, grnxx::LESS_EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  // Test an expresison (TextColumn > "ABC").
+  assert(builder->push_column(&error, "TextColumn"));
+  assert(builder->push_datum(&error, grnxx::Text("ABC")));
+  assert(builder->push_operator(&error, grnxx::GREATER_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison (IntColumn >= 456).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(456)));
+  assert(builder->push_operator(&error, grnxx::GREATER_EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison ((FloatColumn > 0.0) && BoolColumn).
+  assert(builder->push_column(&error, "FloatColumn"));
+  assert(builder->push_datum(&error, grnxx::Float(0.0)));
+  assert(builder->push_operator(&error, grnxx::GREATER_OPERATOR));
+  assert(builder->push_column(&error, "BoolColumn"));
+  assert(builder->push_operator(&error, grnxx::LOGICAL_AND_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison (false || BoolColumn).
+  assert(builder->push_datum(&error, grnxx::Bool(false)));
+  assert(builder->push_column(&error, "BoolColumn"));
+  assert(builder->push_operator(&error, grnxx::LOGICAL_OR_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison (+IntColumn == 123).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_operator(&error, grnxx::POSITIVE_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Int(123)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  // Test an expresison (-IntColumn == 456).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_operator(&error, grnxx::NEGATIVE_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Int(-456)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison (Int(FloatColumn) == 0).
+  assert(builder->push_column(&error, "FloatColumn"));
+  assert(builder->push_operator(&error, grnxx::TO_INT_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Int(0)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 2);
+
+  // Test an expresison (Float(IntColumn) < 300.0).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_operator(&error, grnxx::TO_FLOAT_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Float(300.0)));
+  assert(builder->push_operator(&error, grnxx::LESS_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  // Test an expresison ((IntColumn & 255) == 200).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(255)));
+  assert(builder->push_operator(&error, grnxx::BITWISE_AND_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Int(200)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison ((IntColumn | 256) == 379).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(256)));
+  assert(builder->push_operator(&error, grnxx::BITWISE_OR_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Int(379)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  // Test an expresison ((IntColumn ^ 255) == 132).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(255)));
+  assert(builder->push_operator(&error, grnxx::BITWISE_XOR_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Int(132)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  // Test an expresison ((IntColumn + 100) == 223).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(100)));
+  assert(builder->push_operator(&error, grnxx::PLUS_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Int(223)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  // Test an expresison ((FloatColumn - 0.25) == 0.0).
+  assert(builder->push_column(&error, "FloatColumn"));
+  assert(builder->push_datum(&error, grnxx::Float(0.25)));
+  assert(builder->push_operator(&error, grnxx::MINUS_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Float(0.0)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison ((IntColumn * 2) == 912).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(2)));
+  assert(builder->push_operator(&error, grnxx::MULTIPLICATION_OPERATOR));
+  assert(builder->push_datum(&error, grnxx::Int(912)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->filter(&error, &records));
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 2);
+
+  // Test an expresison (_score + 1.0).
+  assert(builder->push_column(&error, "_score"));
+  assert(builder->push_datum(&error, grnxx::Float(1.0)));
+  assert(builder->push_operator(&error, grnxx::PLUS_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  records.clear();
+  cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) == 2);
+
+  assert(expression->adjust(&error, &records));
+  assert(records.size() == 2);
+  assert(records.get(0).row_id == 1);
+  assert(records.get(0).score == 1.0);
+  assert(records.get(1).row_id == 2);
+  assert(records.get(1).score == 1.0);
+
+  // Test an expresison (IntColumn + 100).
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(100)));
+  assert(builder->push_operator(&error, grnxx::PLUS_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  grnxx::Array<grnxx::Int> result_set;
+  assert(expression->evaluate(&error, records, &result_set));
+  assert(result_set.size() == 2);
+  assert(result_set[0] == 223);
+  assert(result_set[1] == 556);
+}
+
+int main() {
+  test_expression();
+  return 0;
+}

  Deleted: test/test_grnxx.cpp (+0 -1009) 100644
===================================================================
--- test/test_grnxx.cpp    2014-08-12 19:05:59 +0900 (6768b6c)
+++ /dev/null
@@ -1,1009 +0,0 @@
-/*
-  Copyright (C) 2012-2014  Brazil, Inc.
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-#include <cassert>
-#include <iostream>
-#include <random>
-
-#include "grnxx/column.hpp"
-#include "grnxx/cursor.hpp"
-#include "grnxx/datum.hpp"
-#include "grnxx/db.hpp"
-#include "grnxx/error.hpp"
-#include "grnxx/expression.hpp"
-#include "grnxx/sorter.hpp"
-#include "grnxx/table.hpp"
-
-namespace {
-
-void test_db() {
-  grnxx::Error error;
-
-  // デフォルトの設定で空のデータベースを作成する.
-  auto db = grnxx::open_db(&error, "");
-  assert(db);
-  assert(db->num_tables() == 0);
-
-  // "Table_1" という名前のテーブルを作成する.
-  auto table = db->create_table(&error, "Table_1");
-  assert(table);
-  assert(table->name() == "Table_1");
-  assert(db->num_tables() == 1);
-
-  assert(db->get_table(0) == table);
-  assert(db->find_table(&error, "Table_1") == table);
-
-  // 同じ名前でテーブルを作成しようとすると失敗する.
-  assert(!db->create_table(&error, "Table_1"));
-
-  // "Table_2", "Table_3" という名前のテーブルを作成する.
-  assert(db->create_table(&error, "Table_2"));
-  assert(db->create_table(&error, "Table_3"));
-  assert(db->num_tables() == 3);
-
-  // "Table_2" という名前のテーブルを破棄する.
-  assert(db->remove_table(&error, "Table_2"));
-  assert(db->num_tables() == 2);
-
-  assert(db->get_table(0)->name() == "Table_1");
-  assert(db->get_table(1)->name() == "Table_3");
-
-  // "Table_2" という名前のテーブルを作成しなおす.
-  assert(db->create_table(&error, "Table_2"));
-
-  // "Table_3" を "Table_2" の後ろに移動する.
-  assert(db->reorder_table(&error, "Table_3", "Table_2"));
-  assert(db->get_table(0)->name() == "Table_1");
-  assert(db->get_table(1)->name() == "Table_2");
-  assert(db->get_table(2)->name() == "Table_3");
-
-  // "Table_3" を先頭に移動する.
-  assert(db->reorder_table(&error, "Table_3", ""));
-  assert(db->get_table(0)->name() == "Table_3");
-  assert(db->get_table(1)->name() == "Table_1");
-  assert(db->get_table(2)->name() == "Table_2");
-
-  // "Table_2" を "Table_3" の後ろに移動する.
-  assert(db->reorder_table(&error, "Table_2", "Table_3"));
-  assert(db->get_table(0)->name() == "Table_3");
-  assert(db->get_table(1)->name() == "Table_2");
-  assert(db->get_table(2)->name() == "Table_1");
-}
-
-void test_table() {
-  grnxx::Error error;
-
-  auto db = grnxx::open_db(&error, "");
-  assert(db);
-
-  auto table = db->create_table(&error, "Table");
-  assert(table);
-  assert(table->db() == db.get());
-  assert(table->name() == "Table");
-  assert(table->num_columns() == 0);
-  assert(!table->key_column());
-  assert(table->num_rows() == 0);
-  assert(table->max_row_id() == 0);
-
-  // Bool を格納する "Column_1" という名前のカラムを作成する.
-  auto column = table->create_column(&error, "Column_1", grnxx::BOOL_DATA);
-  assert(column);
-  assert(column->name() == "Column_1");
-  assert(table->num_columns() == 1);
-
-  assert(table->get_column(0) == column);
-  assert(table->find_column(&error, "Column_1") == column);
-
-  // 同じ名前でカラムを作成しようとすると失敗する.
-  assert(!table->create_column(&error, "Column_1", grnxx::BOOL_DATA));
-
-  // "Column_2", "Column_3" という名前のカラムを作成する.
-  assert(table->create_column(&error, "Column_2", grnxx::BOOL_DATA));
-  assert(table->create_column(&error, "Column_3", grnxx::BOOL_DATA));
-  assert(table->num_columns() == 3);
-
-  // "Column_2" という名前のカラムを破棄する.
-  assert(table->remove_column(&error, "Column_2"));
-  assert(table->num_columns() == 2);
-
-  assert(table->get_column(0)->name() == "Column_1");
-  assert(table->get_column(1)->name() == "Column_3");
-
-  // "Column_2" という名前のカラムを作成しなおす.
-  assert(table->create_column(&error, "Column_2", grnxx::BOOL_DATA));
-
-  // "Column_3" を "Column_2" の後ろに移動する.
-  assert(table->reorder_column(&error, "Column_3", "Column_2"));
-  assert(table->get_column(0)->name() == "Column_1");
-  assert(table->get_column(1)->name() == "Column_2");
-  assert(table->get_column(2)->name() == "Column_3");
-
-  // "Column_3" を先頭に移動する.
-  assert(table->reorder_column(&error, "Column_3", ""));
-  assert(table->get_column(0)->name() == "Column_3");
-  assert(table->get_column(1)->name() == "Column_1");
-  assert(table->get_column(2)->name() == "Column_2");
-
-  // "Column_2" を "Column_3" の後ろに移動する.
-  assert(table->reorder_column(&error, "Column_2", "Column_3"));
-  assert(table->get_column(0)->name() == "Column_3");
-  assert(table->get_column(1)->name() == "Column_2");
-  assert(table->get_column(2)->name() == "Column_1");
-
-  // TODO: set_key_column(), unset_key_column().
-
-  // 最初の行を追加する.
-  grnxx::Int row_id;
-  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                           grnxx::Datum(), &row_id));
-  assert(row_id == 1);
-  assert(table->num_rows() == 1);
-  assert(table->max_row_id() == 1);
-  assert(!table->test_row(&error, 0));
-  assert(table->test_row(&error, 1));
-  assert(!table->test_row(&error, 2));
-
-  // さらに 2 行追加する.
-  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                           grnxx::Datum(), &row_id));
-  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                           grnxx::Datum(), &row_id));
-  assert(row_id == 3);
-  assert(table->num_rows() == 3);
-  assert(table->max_row_id() == 3);
-  assert(!table->test_row(&error, 0));
-  assert(table->test_row(&error, 1));
-  assert(table->test_row(&error, 2));
-  assert(table->test_row(&error, 3));
-  assert(!table->test_row(&error, 4));
-
-  // 2 番目の行を削除する.
-  assert(table->remove_row(&error, 2));
-  assert(table->num_rows() == 2);
-  assert(table->max_row_id() == 3);
-  assert(!table->test_row(&error, 0));
-  assert(table->test_row(&error, 1));
-  assert(!table->test_row(&error, 2));
-  assert(table->test_row(&error, 3));
-  assert(!table->test_row(&error, 4));
-
-  // TODO: find_row().
-
-  // デフォルト(行 ID 昇順)のカーソルを作成する.
-  auto cursor = table->create_cursor(&error);
-  assert(cursor);
-
-  // カーソルからレコードを読み出す.
-  grnxx::Array<grnxx::Record> records;
-  assert(cursor->read(&error, 0, &records) == 0);
-
-  assert(cursor->read(&error, 1, &records) == 1);
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  assert(cursor->read(&error, 2, &records) == 1);
-  assert(records.size() == 2);
-  assert(records.get(0).row_id == 1);
-  assert(records.get(1).row_id == 3);
-
-  records.clear();
-
-  // 行 ID 降順のカーソルを作成する.
-  grnxx::CursorOptions cursor_options;
-  cursor_options.order_type = grnxx::REVERSE_ORDER;
-  cursor = table->create_cursor(&error, cursor_options);
-  assert(cursor);
-
-  assert(cursor->read_all(&error, &records) == 2);
-  assert(records.size() == 2);
-  assert(records.get(0).row_id == 3);
-  assert(records.get(1).row_id == 1);
-
-  records.clear();
-
-  cursor = table->create_cursor(&error, cursor_options);
-  assert(cursor);
-
-  assert(cursor->read(&error, 1, &records) == 1);
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 3);
-
-  assert(cursor->read(&error, 2, &records) == 1);
-  assert(records.size() == 2);
-  assert(records.get(0).row_id == 3);
-  assert(records.get(1).row_id == 1);
-}
-
-void test_bitmap() {
-  constexpr int NUM_ROWS = 1 << 14;
-
-  grnxx::Error error;
-
-  auto db = grnxx::open_db(&error, "");
-  assert(db);
-
-  auto table = db->create_table(&error, "Table");
-  assert(table);
-
-  for (int i = 0; i < NUM_ROWS; ++i) {
-    grnxx::Int row_id;
-    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                             grnxx::Datum(), &row_id));
-    assert(row_id == (i + 1));
-  }
-  assert(table->num_rows() == NUM_ROWS);
-  assert(table->max_row_id() == NUM_ROWS);
-
-  for (int i = 0; i < NUM_ROWS; ++i) {
-    grnxx::Int row_id = i + 1;
-    assert(table->remove_row(&error, row_id));
-  }
-  assert(table->num_rows() == 0);
-  assert(table->max_row_id() == (grnxx::MIN_ROW_ID - 1));
-
-  for (int i = 0; i < NUM_ROWS; ++i) {
-    grnxx::Int row_id;
-    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                             grnxx::Datum(), &row_id));
-    assert(row_id == (i + 1));
-  }
-  assert(table->num_rows() == NUM_ROWS);
-  assert(table->max_row_id() == NUM_ROWS);
-
-  for (int i = 0; i < NUM_ROWS; i += 2) {
-    grnxx::Int row_id = i + 1;
-    assert(table->remove_row(&error, row_id));
-  }
-  assert(table->num_rows() == (NUM_ROWS / 2));
-  assert(table->max_row_id() == NUM_ROWS);
-
-  for (int i = 0; i < NUM_ROWS; i += 2) {
-    grnxx::Int row_id;
-    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                             grnxx::Datum(), &row_id));
-    assert(row_id == (i + 1));
-  }
-  assert(table->num_rows() == NUM_ROWS);
-  assert(table->max_row_id() == NUM_ROWS);
-
-  for (int i = 0; i < NUM_ROWS; ++i) {
-    grnxx::Int row_id = NUM_ROWS - i;
-    assert(table->remove_row(&error, row_id));
-    assert(table->max_row_id() == (row_id - 1));
-  }
-}
-
-void test_column() {
-  grnxx::Error error;
-
-  auto db = grnxx::open_db(&error, "");
-  assert(db);
-
-  auto table = db->create_table(&error, "Table");
-  assert(table);
-
-  // 最初の行を追加する.
-  grnxx::Int row_id;
-  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                           grnxx::Datum(), &row_id));
-
-  // Bool を格納する "BoolColumn" という名前のカラムを作成する.
-  auto bool_column = table->create_column(&error, "BoolColumn",
-                                          grnxx::BOOL_DATA);
-  assert(bool_column);
-  assert(bool_column->table() == table);
-  assert(bool_column->name() == "BoolColumn");
-  assert(bool_column->data_type() == grnxx::BOOL_DATA);
-  assert(!bool_column->has_key_attribute());
-  assert(bool_column->num_indexes() == 0);
-
-  // Int を格納する "IntColumn" という名前のカラムを作成する.
-  auto int_column = table->create_column(&error, "IntColumn",
-                                         grnxx::INT_DATA);
-  assert(int_column);
-  assert(int_column->table() == table);
-  assert(int_column->name() == "IntColumn");
-  assert(int_column->data_type() == grnxx::INT_DATA);
-  assert(!int_column->has_key_attribute());
-  assert(int_column->num_indexes() == 0);
-
-  // Float を格納する "FloatColumn" という名前のカラムを作成する.
-  auto float_column = table->create_column(&error, "FloatColumn",
-                                           grnxx::FLOAT_DATA);
-  assert(float_column);
-  assert(float_column->table() == table);
-  assert(float_column->name() == "FloatColumn");
-  assert(float_column->data_type() == grnxx::FLOAT_DATA);
-  assert(!float_column->has_key_attribute());
-  assert(float_column->num_indexes() == 0);
-
-  // Text を格納する "TextColumn" という名前のカラムを作成する.
-  auto text_column = table->create_column(&error, "TextColumn",
-                                           grnxx::TEXT_DATA);
-  assert(text_column);
-  assert(text_column->table() == table);
-  assert(text_column->name() == "TextColumn");
-  assert(text_column->data_type() == grnxx::TEXT_DATA);
-  assert(!text_column->has_key_attribute());
-  assert(text_column->num_indexes() == 0);
-
-  grnxx::Datum datum;
-
-  // 最初の行にデフォルト値が格納されていることを確認する.
-  assert(bool_column->get(&error, 1, &datum));
-  assert(datum.type() == grnxx::BOOL_DATA);
-  assert(!datum.force_bool());
-
-  assert(int_column->get(&error, 1, &datum));
-  assert(datum.type() == grnxx::INT_DATA);
-  assert(datum.force_int() == 0);
-
-  assert(float_column->get(&error, 1, &datum));
-  assert(datum.type() == grnxx::FLOAT_DATA);
-  assert(datum.force_float() == 0.0);
-
-  assert(text_column->get(&error, 1, &datum));
-  assert(datum.type() == grnxx::TEXT_DATA);
-  assert(datum.force_text() == "");
-
-  // 最初の行に正しく値を格納できるか確認する.
-  assert(bool_column->set(&error, 1, grnxx::Bool(true)));
-  assert(int_column->set(&error, 1, grnxx::Int(123)));
-  assert(float_column->set(&error, 1, grnxx::Float(0.25)));
-  assert(text_column->set(&error, 1, grnxx::Text("Hello, world!")));
-
-  assert(bool_column->get(&error, 1, &datum));
-  assert(datum.type() == grnxx::BOOL_DATA);
-  assert(datum.force_bool());
-
-  assert(int_column->get(&error, 1, &datum));
-  assert(datum.type() == grnxx::INT_DATA);
-  assert(datum.force_int() == 123);
-
-  assert(float_column->get(&error, 1, &datum));
-  assert(datum.type() == grnxx::FLOAT_DATA);
-  assert(datum.force_float() == 0.25);
-
-  assert(text_column->get(&error, 1, &datum));
-  assert(datum.type() == grnxx::TEXT_DATA);
-  assert(datum.force_text() == "Hello, world!");
-}
-
-void test_expression() {
-  grnxx::Error error;
-
-  auto db = grnxx::open_db(&error, "");
-  assert(db);
-
-  auto table = db->create_table(&error, "Table");
-  assert(table);
-
-  auto bool_column = table->create_column(&error, "BoolColumn",
-                                          grnxx::BOOL_DATA);
-  assert(bool_column);
-
-  auto int_column = table->create_column(&error, "IntColumn",
-                                         grnxx::INT_DATA);
-  assert(int_column);
-
-  auto float_column = table->create_column(&error, "FloatColumn",
-                                           grnxx::FLOAT_DATA);
-  assert(float_column);
-
-  auto text_column = table->create_column(&error, "TextColumn",
-                                           grnxx::TEXT_DATA);
-  assert(text_column);
-
-  // 下記のデータを格納する.
-  //
-  // RowID BoolColumn IntColumn FloatColumn TextColumn
-  //     1      false       123       -0.25      "ABC"
-  //     2       true       456        0.25      "XYZ"
-  grnxx::Int row_id;
-  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                           grnxx::Datum(), &row_id));
-  assert(bool_column->set(&error, row_id, grnxx::Bool(false)));
-  assert(int_column->set(&error, row_id, grnxx::Int(123)));
-  assert(float_column->set(&error, row_id, grnxx::Float(-0.25)));
-  assert(text_column->set(&error, row_id, grnxx::Text("ABC")));
-
-  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                           grnxx::Datum(), &row_id));
-  assert(bool_column->set(&error, row_id, grnxx::Bool(true)));
-  assert(int_column->set(&error, row_id, grnxx::Int(456)));
-  assert(float_column->set(&error, row_id, grnxx::Float(0.25)));
-  assert(text_column->set(&error, row_id, grnxx::Text("XYZ")));
-
-  // 式構築用のオブジェクトを用意する.
-  auto builder = grnxx::ExpressionBuilder::create(&error, table);
-  assert(builder);
-
-  // もっとも単純な恒真式を作成する.
-  assert(builder->push_datum(&error, grnxx::Bool(true)));
-  auto expression = builder->release(&error);
-  assert(expression);
-
-  // 恒真式のフィルタにかけても変化しないことを確認する.
-  grnxx::Array<grnxx::Record> records;
-  auto cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 2);
-
-  // 演算子を含む恒真式を作成する.
-  assert(builder->push_datum(&error, grnxx::Int(100)));
-  assert(builder->push_datum(&error, grnxx::Int(100)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 2);
-
-  // "BoolColumn" という名前のカラムに格納されている値を返すだけの式を作成する.
-  assert(builder->push_column(&error, "BoolColumn"));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // "IntColumn" という名前のカラムに格納されている値が 123 のときだけ
-  // 真になる式を作成する.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(123)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // "IntColumn" という名前のカラムに格納されている値が 123 でないときだけ
-  // 真になる式を作成する.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(123)));
-  assert(builder->push_operator(&error, grnxx::NOT_EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 大小関係による比較を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(300)));
-  assert(builder->push_operator(&error, grnxx::LESS_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 大小関係による比較を試す.
-  assert(builder->push_column(&error, "TextColumn"));
-  assert(builder->push_datum(&error, grnxx::Text("ABC")));
-  assert(builder->push_operator(&error, grnxx::LESS_EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 大小関係による比較を試す.
-  assert(builder->push_column(&error, "TextColumn"));
-  assert(builder->push_datum(&error, grnxx::Text("ABC")));
-  assert(builder->push_operator(&error, grnxx::GREATER_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 大小関係による比較を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(456)));
-  assert(builder->push_operator(&error, grnxx::GREATER_EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 論理演算を試す.
-  assert(builder->push_column(&error, "FloatColumn"));
-  assert(builder->push_datum(&error, grnxx::Float(0.0)));
-  assert(builder->push_operator(&error, grnxx::GREATER_OPERATOR));
-  assert(builder->push_column(&error, "BoolColumn"));
-  assert(builder->push_operator(&error, grnxx::LOGICAL_AND_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 論理演算を試す.
-  assert(builder->push_datum(&error, grnxx::Bool(false)));
-  assert(builder->push_column(&error, "BoolColumn"));
-  assert(builder->push_operator(&error, grnxx::LOGICAL_OR_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 符号(+)を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_operator(&error, grnxx::POSITIVE_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Int(123)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 符号(-)を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_operator(&error, grnxx::NEGATIVE_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Int(-456)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 型変換(整数)を試す.
-  assert(builder->push_column(&error, "FloatColumn"));
-  assert(builder->push_operator(&error, grnxx::TO_INT_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Int(0)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 型変換(浮動小数点数)を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_operator(&error, grnxx::TO_FLOAT_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Float(300.0)));
-  assert(builder->push_operator(&error, grnxx::LESS_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // ビット論理積を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(255)));
-  assert(builder->push_operator(&error, grnxx::BITWISE_AND_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Int(200)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // ビット論理和を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(256)));
-  assert(builder->push_operator(&error, grnxx::BITWISE_OR_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Int(379)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // ビット排他的論理和を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(255)));
-  assert(builder->push_operator(&error, grnxx::BITWISE_XOR_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Int(132)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 加算を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(100)));
-  assert(builder->push_operator(&error, grnxx::PLUS_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Int(223)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 1);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 減算を試す.
-  assert(builder->push_column(&error, "FloatColumn"));
-  assert(builder->push_datum(&error, grnxx::Float(0.25)));
-  assert(builder->push_operator(&error, grnxx::MINUS_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Float(0.0)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 乗算を試す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(2)));
-  assert(builder->push_operator(&error, grnxx::MULTIPLICATION_OPERATOR));
-  assert(builder->push_datum(&error, grnxx::Int(912)));
-  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // フィルタとして使ったときの結果を確認する.
-  assert(expression->filter(&error, &records));
-  assert(records.size() == 1);
-  assert(records.get(0).row_id == 2);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // スコア計算を試す.
-  assert(builder->push_column(&error, "_score"));
-  assert(builder->push_datum(&error, grnxx::Float(1.0)));
-  assert(builder->push_operator(&error, grnxx::PLUS_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // スコア調整に使ったときの結果を確認する.
-  assert(expression->adjust(&error, &records));
-  assert(records.size() == 2);
-  assert(records.get(0).row_id == 1);
-  assert(records.get(0).score == 1.0);
-  assert(records.get(1).row_id == 2);
-  assert(records.get(1).score == 1.0);
-
-  records.clear();
-  cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) == 2);
-
-  // 評価結果を取り出す.
-  assert(builder->push_column(&error, "IntColumn"));
-  assert(builder->push_datum(&error, grnxx::Int(100)));
-  assert(builder->push_operator(&error, grnxx::PLUS_OPERATOR));
-  expression = builder->release(&error);
-  assert(expression);
-
-  // 評価結果を確認する.
-  grnxx::Array<grnxx::Int> result_set;
-  assert(expression->evaluate(&error, records, &result_set));
-  assert(result_set.size() == 2);
-  assert(result_set[0] == 223);
-  assert(result_set[1] == 556);
-}
-
-void test_sorter() {
-  grnxx::Error error;
-
-  auto db = grnxx::open_db(&error, "");
-  assert(db);
-
-  auto table = db->create_table(&error, "Table");
-  assert(table);
-
-  auto bool_column = table->create_column(&error, "BoolColumn",
-                                          grnxx::BOOL_DATA);
-  assert(bool_column);
-
-  auto int_column = table->create_column(&error, "IntColumn",
-                                         grnxx::INT_DATA);
-  assert(int_column);
-
-  // 擬似乱数生成器を使って [0, 64) に収まる 1024 個の整数を登録する.
-  constexpr size_t NUM_VALUES = 1024;
-  std::vector<grnxx::Bool> bool_values(NUM_VALUES);
-  std::vector<grnxx::Int> int_values(NUM_VALUES);
-  std::mt19937_64 mersenne_twister;
-  for (size_t i = 0; i < bool_values.size(); ++i) {
-    grnxx::Int row_id;
-    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
-                             grnxx::Datum(), &row_id));
-    bool_values[i] = (mersenne_twister() & 1) != 0;
-    int_values[i] = mersenne_twister() % 64;
-    assert(bool_column->set(&error, row_id, grnxx::Bool(bool_values[i])));
-    assert(int_column->set(&error, row_id, int_values[i]));
-  }
-
-  grnxx::Array<grnxx::Record> records;
-  auto cursor = table->create_cursor(&error);
-  assert(cursor);
-  assert(cursor->read_all(&error, &records) ==
-         static_cast<grnxx::Int>(int_values.size()));
-  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
-
-  // BoolColumn 昇順,行 ID 昇順に整列する.
-  grnxx::Array<grnxx::SortOrder> orders;
-  assert(orders.resize(&error, 2));
-  auto expression_builder =
-      grnxx::ExpressionBuilder::create(&error, table);
-  assert(expression_builder->push_column(&error, "BoolColumn"));
-  auto expression = expression_builder->release(&error);
-  assert(expression);
-  orders[0].expression = std::move(expression);
-
-  assert(expression_builder->push_column(&error, "_id"));
-  expression = expression_builder->release(&error);
-  assert(expression);
-  orders[1].expression = std::move(expression);
-
-  auto sorter = grnxx::Sorter::create(&error, std::move(orders));
-  assert(sorter);
-
-  assert(sorter->sort(&error, &records));
-  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
-
-  for (grnxx::Int i = 1; i < records.size(); ++i) {
-    grnxx::Int lhs_id = records.get_row_id(i - 1) - 1;
-    grnxx::Int rhs_id = records.get_row_id(i) - 1;
-    grnxx::Bool lhs_value = bool_values[lhs_id];
-    grnxx::Bool rhs_value = bool_values[rhs_id];
-    assert(!lhs_value || rhs_value);
-    if (lhs_value == rhs_value) {
-      assert(lhs_id < rhs_id);
-    }
-  }
-
-  // BoolColumn 降順,行 ID 降順に整列する.
-  assert(orders.resize(&error, 2));
-  expression_builder = grnxx::ExpressionBuilder::create(&error, table);
-  assert(expression_builder->push_column(&error, "BoolColumn"));
-  expression = expression_builder->release(&error);
-  assert(expression);
-  orders[0].expression = std::move(expression);
-  orders[0].type = grnxx::REVERSE_ORDER;
-
-  assert(expression_builder->push_column(&error, "_id"));
-  expression = expression_builder->release(&error);
-  assert(expression);
-  orders[1].expression = std::move(expression);
-  orders[1].type = grnxx::REVERSE_ORDER;
-
-  sorter = grnxx::Sorter::create(&error, std::move(orders));
-  assert(sorter);
-
-  assert(sorter->sort(&error, &records));
-  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
-
-  for (grnxx::Int i = 1; i < records.size(); ++i) {
-    grnxx::Int lhs_id = records.get_row_id(i - 1) - 1;
-    grnxx::Int rhs_id = records.get_row_id(i) - 1;
-    grnxx::Bool lhs_value = bool_values[lhs_id];
-    grnxx::Bool rhs_value = bool_values[rhs_id];
-    assert(lhs_value || !rhs_value);
-    if (lhs_value == rhs_value) {
-      assert(lhs_id > rhs_id);
-    }
-  }
-
-  // IntColumn 昇順,行 ID 昇順に整列する.
-  assert(orders.resize(&error, 2));
-  expression_builder = grnxx::ExpressionBuilder::create(&error, table);
-  assert(expression_builder->push_column(&error, "IntColumn"));
-  expression = expression_builder->release(&error);
-  orders[0].expression = std::move(expression);
-
-  assert(expression_builder->push_column(&error, "_id"));
-  expression = expression_builder->release(&error);
-  assert(expression);
-  orders[1].expression = std::move(expression);
-
-  sorter = grnxx::Sorter::create(&error, std::move(orders));
-  assert(sorter);
-
-  assert(sorter->sort(&error, &records));
-  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
-
-  for (grnxx::Int i = 1; i < records.size(); ++i) {
-    grnxx::Int lhs_id = records.get_row_id(i - 1) - 1;
-    grnxx::Int rhs_id = records.get_row_id(i) - 1;
-    grnxx::Int lhs_value = int_values[lhs_id];
-    grnxx::Int rhs_value = int_values[rhs_id];
-    assert(lhs_value <= rhs_value);
-    if (lhs_value == rhs_value) {
-      assert(lhs_id < rhs_id);
-    }
-  }
-
-  // IntColumn 降順,行 ID 降順に整列する.
-  assert(orders.resize(&error, 2));
-  assert(expression_builder->push_column(&error, "IntColumn"));
-  expression = expression_builder->release(&error);
-  assert(expression);
-  orders[0].expression = std::move(expression);
-  orders[0].type = grnxx::REVERSE_ORDER;
-
-  assert(expression_builder->push_column(&error, "_id"));
-  expression = expression_builder->release(&error);
-  assert(expression);
-  orders[1].expression = std::move(expression);
-  orders[1].type = grnxx::REVERSE_ORDER;
-
-  sorter = grnxx::Sorter::create(&error, std::move(orders));
-  assert(sorter);
-
-  assert(sorter->sort(&error, &records));
-  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
-
-  for (grnxx::Int i = 1; i < records.size(); ++i) {
-    grnxx::Int lhs_id = records.get_row_id(i - 1) - 1;
-    grnxx::Int rhs_id = records.get_row_id(i) - 1;
-    grnxx::Int lhs_value = int_values[lhs_id];
-    grnxx::Int rhs_value = int_values[rhs_id];
-    assert(lhs_value >= rhs_value);
-    if (lhs_value == rhs_value) {
-      assert(lhs_id > rhs_id);
-    }
-  }
-}
-
-}  // namespace
-
-int main() {
-  test_db();
-  test_table();
-  test_bitmap();
-  test_column();
-  test_expression();
-  test_sorter();
-
-  return 0;
-}

  Added: test/test_sorter.cpp (+202 -0) 100644
===================================================================
--- /dev/null
+++ test/test_sorter.cpp    2014-08-18 11:30:17 +0900 (b23c744)
@@ -0,0 +1,202 @@
+/*
+  Copyright (C) 2012-2014  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <cassert>
+#include <iostream>
+#include <random>
+
+#include "grnxx/column.hpp"
+#include "grnxx/cursor.hpp"
+#include "grnxx/datum.hpp"
+#include "grnxx/db.hpp"
+#include "grnxx/error.hpp"
+#include "grnxx/expression.hpp"
+#include "grnxx/table.hpp"
+#include "grnxx/sorter.hpp"
+
+void test_sorter() {
+  grnxx::Error error;
+
+  // Create a database with the default options.
+  auto db = grnxx::open_db(&error, "");
+  assert(db);
+
+  // Create a table with the default options.
+  auto table = db->create_table(&error, "Table");
+  assert(table);
+
+  // Create a Bool column named "BoolColumn".
+  auto bool_column = table->create_column(&error, "BoolColumn",
+                                          grnxx::BOOL_DATA);
+  assert(bool_column);
+
+  // Create a Int column named "IntColumn".
+  auto int_column = table->create_column(&error, "IntColumn",
+                                         grnxx::INT_DATA);
+  assert(int_column);
+
+  // Generate 1,024 random integers in a range [0, 64).
+  constexpr size_t NUM_VALUES = 1024;
+  std::vector<grnxx::Bool> bool_values(NUM_VALUES);
+  std::vector<grnxx::Int> int_values(NUM_VALUES);
+  std::mt19937_64 mersenne_twister;
+  for (size_t i = 0; i < bool_values.size(); ++i) {
+    grnxx::Int row_id;
+    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                             grnxx::Datum(), &row_id));
+    bool_values[i] = (mersenne_twister() & 1) != 0;
+    int_values[i] = mersenne_twister() % 64;
+    assert(bool_column->set(&error, row_id, grnxx::Bool(bool_values[i])));
+    assert(int_column->set(&error, row_id, int_values[i]));
+  }
+
+  grnxx::Array<grnxx::Record> records;
+  auto cursor = table->create_cursor(&error);
+  assert(cursor);
+  assert(cursor->read_all(&error, &records) ==
+         static_cast<grnxx::Int>(int_values.size()));
+  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
+
+  // Sort records in order of BoolColumn value and row ID.
+  grnxx::Array<grnxx::SortOrder> orders;
+  assert(orders.resize(&error, 2));
+  auto expression_builder =
+      grnxx::ExpressionBuilder::create(&error, table);
+  assert(expression_builder->push_column(&error, "BoolColumn"));
+  auto expression = expression_builder->release(&error);
+  assert(expression);
+  orders[0].expression = std::move(expression);
+
+  assert(expression_builder->push_column(&error, "_id"));
+  expression = expression_builder->release(&error);
+  assert(expression);
+  orders[1].expression = std::move(expression);
+
+  auto sorter = grnxx::Sorter::create(&error, std::move(orders));
+  assert(sorter);
+
+  assert(sorter->sort(&error, &records));
+  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
+
+  for (grnxx::Int i = 1; i < records.size(); ++i) {
+    grnxx::Int lhs_id = records.get_row_id(i - 1) - 1;
+    grnxx::Int rhs_id = records.get_row_id(i) - 1;
+    grnxx::Bool lhs_value = bool_values[lhs_id];
+    grnxx::Bool rhs_value = bool_values[rhs_id];
+    assert(!lhs_value || rhs_value);
+    if (lhs_value == rhs_value) {
+      assert(lhs_id < rhs_id);
+    }
+  }
+
+  // Sort records in reverse order of BoolColumn value and row ID.
+  assert(orders.resize(&error, 2));
+  expression_builder = grnxx::ExpressionBuilder::create(&error, table);
+  assert(expression_builder->push_column(&error, "BoolColumn"));
+  expression = expression_builder->release(&error);
+  assert(expression);
+  orders[0].expression = std::move(expression);
+  orders[0].type = grnxx::REVERSE_ORDER;
+
+  assert(expression_builder->push_column(&error, "_id"));
+  expression = expression_builder->release(&error);
+  assert(expression);
+  orders[1].expression = std::move(expression);
+  orders[1].type = grnxx::REVERSE_ORDER;
+
+  sorter = grnxx::Sorter::create(&error, std::move(orders));
+  assert(sorter);
+
+  assert(sorter->sort(&error, &records));
+  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
+
+  for (grnxx::Int i = 1; i < records.size(); ++i) {
+    grnxx::Int lhs_id = records.get_row_id(i - 1) - 1;
+    grnxx::Int rhs_id = records.get_row_id(i) - 1;
+    grnxx::Bool lhs_value = bool_values[lhs_id];
+    grnxx::Bool rhs_value = bool_values[rhs_id];
+    assert(lhs_value || !rhs_value);
+    if (lhs_value == rhs_value) {
+      assert(lhs_id > rhs_id);
+    }
+  }
+
+  // Sort records in order of IntColumn value and row ID.
+  assert(orders.resize(&error, 2));
+  expression_builder = grnxx::ExpressionBuilder::create(&error, table);
+  assert(expression_builder->push_column(&error, "IntColumn"));
+  expression = expression_builder->release(&error);
+  orders[0].expression = std::move(expression);
+
+  assert(expression_builder->push_column(&error, "_id"));
+  expression = expression_builder->release(&error);
+  assert(expression);
+  orders[1].expression = std::move(expression);
+
+  sorter = grnxx::Sorter::create(&error, std::move(orders));
+  assert(sorter);
+
+  assert(sorter->sort(&error, &records));
+  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
+
+  for (grnxx::Int i = 1; i < records.size(); ++i) {
+    grnxx::Int lhs_id = records.get_row_id(i - 1) - 1;
+    grnxx::Int rhs_id = records.get_row_id(i) - 1;
+    grnxx::Int lhs_value = int_values[lhs_id];
+    grnxx::Int rhs_value = int_values[rhs_id];
+    assert(lhs_value <= rhs_value);
+    if (lhs_value == rhs_value) {
+      assert(lhs_id < rhs_id);
+    }
+  }
+
+  // Sort records in reverse order of IntColumn value and row ID.
+  assert(orders.resize(&error, 2));
+  assert(expression_builder->push_column(&error, "IntColumn"));
+  expression = expression_builder->release(&error);
+  assert(expression);
+  orders[0].expression = std::move(expression);
+  orders[0].type = grnxx::REVERSE_ORDER;
+
+  assert(expression_builder->push_column(&error, "_id"));
+  expression = expression_builder->release(&error);
+  assert(expression);
+  orders[1].expression = std::move(expression);
+  orders[1].type = grnxx::REVERSE_ORDER;
+
+  sorter = grnxx::Sorter::create(&error, std::move(orders));
+  assert(sorter);
+
+  assert(sorter->sort(&error, &records));
+  assert(records.size() == static_cast<grnxx::Int>(int_values.size()));
+
+  for (grnxx::Int i = 1; i < records.size(); ++i) {
+    grnxx::Int lhs_id = records.get_row_id(i - 1) - 1;
+    grnxx::Int rhs_id = records.get_row_id(i) - 1;
+    grnxx::Int lhs_value = int_values[lhs_id];
+    grnxx::Int rhs_value = int_values[rhs_id];
+    assert(lhs_value >= rhs_value);
+    if (lhs_value == rhs_value) {
+      assert(lhs_id > rhs_id);
+    }
+  }
+}
+
+int main() {
+  test_sorter();
+  return 0;
+}

  Added: test/test_table.cpp (+296 -0) 100644
===================================================================
--- /dev/null
+++ test/test_table.cpp    2014-08-18 11:30:17 +0900 (b84d68d)
@@ -0,0 +1,296 @@
+/*
+  Copyright (C) 2012-2014  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <cassert>
+#include <iostream>
+
+#include "grnxx/column.hpp"
+#include "grnxx/cursor.hpp"
+#include "grnxx/datum.hpp"
+#include "grnxx/db.hpp"
+#include "grnxx/error.hpp"
+#include "grnxx/table.hpp"
+
+void test_table() {
+  grnxx::Error error;
+
+  // Create a database with the default options.
+  auto db = grnxx::open_db(&error, "");
+  assert(db);
+
+  // Create a table named "Table".
+  auto table = db->create_table(&error, "Table");
+  assert(table);
+  assert(table->db() == db.get());
+  assert(table->name() == "Table");
+  assert(table->num_columns() == 0);
+  assert(!table->key_column());
+  assert(table->num_rows() == 0);
+  assert(table->max_row_id() == 0);
+
+  // Create a column named "Column_1".
+  auto column = table->create_column(&error, "Column_1", grnxx::BOOL_DATA);
+  assert(column);
+  assert(column->name() == "Column_1");
+  assert(table->num_columns() == 1);
+
+  assert(table->get_column(0) == column);
+  assert(table->find_column(&error, "Column_1") == column);
+
+  // The following create_column() must fail because "Column_1" already exists.
+  assert(!table->create_column(&error, "Column_1", grnxx::BOOL_DATA));
+
+  // Create columns named "Column_2" and Column_3".
+  assert(table->create_column(&error, "Column_2", grnxx::BOOL_DATA));
+  assert(table->create_column(&error, "Column_3", grnxx::BOOL_DATA));
+  assert(table->num_columns() == 3);
+
+  // Remove "Column_2".
+  assert(table->remove_column(&error, "Column_2"));
+  assert(table->num_columns() == 2);
+
+  assert(table->get_column(0)->name() == "Column_1");
+  assert(table->get_column(1)->name() == "Column_3");
+
+  // Recreate "Column_2".
+  assert(table->create_column(&error, "Column_2", grnxx::BOOL_DATA));
+
+  // Move "Column_3" to the next to "Column_2".
+  assert(table->reorder_column(&error, "Column_3", "Column_2"));
+  assert(table->get_column(0)->name() == "Column_1");
+  assert(table->get_column(1)->name() == "Column_2");
+  assert(table->get_column(2)->name() == "Column_3");
+
+  // Move "Column_3" to the head.
+  assert(table->reorder_column(&error, "Column_3", ""));
+  assert(table->get_column(0)->name() == "Column_3");
+  assert(table->get_column(1)->name() == "Column_1");
+  assert(table->get_column(2)->name() == "Column_2");
+
+  // Move "Column_2" to the next to "Column3".
+  assert(table->reorder_column(&error, "Column_2", "Column_3"));
+  assert(table->get_column(0)->name() == "Column_3");
+  assert(table->get_column(1)->name() == "Column_2");
+  assert(table->get_column(2)->name() == "Column_1");
+}
+
+void test_rows() {
+  grnxx::Error error;
+
+  // Create a database with the default options.
+  auto db = grnxx::open_db(&error, "");
+  assert(db);
+
+  // Create a table named "Table".
+  auto table = db->create_table(&error, "Table");
+  assert(table);
+
+  // Append the first row.
+  grnxx::Int row_id;
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(row_id == 1);
+  assert(table->num_rows() == 1);
+  assert(table->max_row_id() == 1);
+  assert(!table->test_row(&error, 0));
+  assert(table->test_row(&error, 1));
+  assert(!table->test_row(&error, 2));
+
+  // Append two more rows.
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(row_id == 3);
+  assert(table->num_rows() == 3);
+  assert(table->max_row_id() == 3);
+  assert(!table->test_row(&error, 0));
+  assert(table->test_row(&error, 1));
+  assert(table->test_row(&error, 2));
+  assert(table->test_row(&error, 3));
+  assert(!table->test_row(&error, 4));
+
+  // Remove the 2nd row.
+  assert(table->remove_row(&error, 2));
+  assert(table->num_rows() == 2);
+  assert(table->max_row_id() == 3);
+  assert(!table->test_row(&error, 0));
+  assert(table->test_row(&error, 1));
+  assert(!table->test_row(&error, 2));
+  assert(table->test_row(&error, 3));
+  assert(!table->test_row(&error, 4));
+}
+
+void test_bitmap() {
+  constexpr int NUM_ROWS = 1 << 16;
+
+  grnxx::Error error;
+
+  // Create a database with the default options.
+  auto db = grnxx::open_db(&error, "");
+  assert(db);
+
+  // Create a table named "Table".
+  auto table = db->create_table(&error, "Table");
+  assert(table);
+
+  // Create rows.
+  for (int i = 0; i < NUM_ROWS; ++i) {
+    grnxx::Int row_id;
+    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                             grnxx::Datum(), &row_id));
+    assert(row_id == (i + 1));
+  }
+  assert(table->num_rows() == NUM_ROWS);
+  assert(table->max_row_id() == NUM_ROWS);
+
+  // Remove all rows.
+  for (int i = 0; i < NUM_ROWS; ++i) {
+    grnxx::Int row_id = i + 1;
+    assert(table->remove_row(&error, row_id));
+  }
+  assert(table->num_rows() == 0);
+  assert(table->max_row_id() == (grnxx::MIN_ROW_ID - 1));
+
+  // Recreate rows.
+  for (int i = 0; i < NUM_ROWS; ++i) {
+    grnxx::Int row_id;
+    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                             grnxx::Datum(), &row_id));
+    assert(row_id == (i + 1));
+  }
+  assert(table->num_rows() == NUM_ROWS);
+  assert(table->max_row_id() == NUM_ROWS);
+
+  // Remove rows with odd IDs.
+  for (int i = 0; i < NUM_ROWS; i += 2) {
+    grnxx::Int row_id = i + 1;
+    assert(table->remove_row(&error, row_id));
+  }
+  assert(table->num_rows() == (NUM_ROWS / 2));
+  assert(table->max_row_id() == NUM_ROWS);
+
+  // Recreate rows.
+  for (int i = 0; i < NUM_ROWS; i += 2) {
+    grnxx::Int row_id;
+    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                             grnxx::Datum(), &row_id));
+    assert(row_id == (i + 1));
+  }
+  assert(table->num_rows() == NUM_ROWS);
+  assert(table->max_row_id() == NUM_ROWS);
+
+  // Remove rows in reverse order.
+  for (int i = 0; i < NUM_ROWS; ++i) {
+    grnxx::Int row_id = NUM_ROWS - i;
+    assert(table->remove_row(&error, row_id));
+    assert(table->max_row_id() == (row_id - 1));
+  }
+
+  // Recreate rows.
+  for (int i = 0; i < NUM_ROWS; ++i) {
+    grnxx::Int row_id;
+    assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                             grnxx::Datum(), &row_id));
+    assert(row_id == (i + 1));
+  }
+  assert(table->num_rows() == NUM_ROWS);
+  assert(table->max_row_id() == NUM_ROWS);
+}
+
+void test_key() {
+  // TODO: set_key_column(), unset_key_column(), and find_row() are not
+  //       supported yet.
+}
+
+void test_cursor() {
+  grnxx::Error error;
+
+  // Create a database with the default options.
+  auto db = grnxx::open_db(&error, "");
+  assert(db);
+
+  // Create a table named "Table".
+  auto table = db->create_table(&error, "Table");
+  assert(table);
+
+  // Create a column named "Column".
+  assert(table->create_column(&error, "Column", grnxx::BOOL_DATA));
+
+  // Append three rows and remove the 2nd row.
+  grnxx::Int row_id;
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(table->remove_row(&error, 2));
+
+  // Create a cursor with the default options.
+  auto cursor = table->create_cursor(&error);
+  assert(cursor);
+
+  // Read records from the cursor.
+  grnxx::Array<grnxx::Record> records;
+  assert(cursor->read(&error, 0, &records) == 0);
+
+  assert(cursor->read(&error, 1, &records) == 1);
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 1);
+
+  assert(cursor->read(&error, 2, &records) == 1);
+  assert(records.size() == 2);
+  assert(records.get(0).row_id == 1);
+  assert(records.get(1).row_id == 3);
+
+  records.clear();
+
+  // Create a cursor that scans a table in reverse order.
+  grnxx::CursorOptions cursor_options;
+  cursor_options.order_type = grnxx::REVERSE_ORDER;
+  cursor = table->create_cursor(&error, cursor_options);
+  assert(cursor);
+
+  assert(cursor->read_all(&error, &records) == 2);
+  assert(records.size() == 2);
+  assert(records.get(0).row_id == 3);
+  assert(records.get(1).row_id == 1);
+
+  records.clear();
+
+  cursor = table->create_cursor(&error, cursor_options);
+  assert(cursor);
+
+  assert(cursor->read(&error, 1, &records) == 1);
+  assert(records.size() == 1);
+  assert(records.get(0).row_id == 3);
+
+  assert(cursor->read(&error, 2, &records) == 1);
+  assert(records.size() == 2);
+  assert(records.get(0).row_id == 3);
+  assert(records.get(1).row_id == 1);
+}
+
+int main() {
+  test_table();
+  test_rows();
+  test_bitmap();
+  test_key();
+  test_cursor();
+  return 0;
+}




More information about the Groonga-commit mailing list
Back to archive index