/*
  Copyright (C) 2010-2021  Sutou Kouhei <kou@clear-code.com>

  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 <gcutter.h>
#include <glib/gstdio.h>

#include "../lib/grn-assertions.h"

#include <grn_str.h>
#include <grn_util.h>

#define get(name) grn_ctx_get(context, name, strlen(name))

void test_null(void);
void test_void(void);
void test_int8(void);
void test_int16(void);
void test_int32(void);
void test_int64(void);
void test_uint8(void);
void test_uint16(void);
void test_uint32(void);
void test_uint64(void);
void test_float(void);
void test_time(void);
void test_bool_true(void);
void test_bool_false(void);
void test_text(void);
void test_geo_point_tokyo(void);
void test_geo_point_wgs84(void);
void test_array_empty(void);
void test_array_with_records(void);
void test_hash_empty(void);
void test_hash_with_records(void);
void test_patricia_trie_empty(void);
void test_patricia_trie_with_records(void);
void test_patricia_trie_cursor_empty(void);
void test_patricia_trie_cursor_with_records(void);
void test_ptr_empty(void);
void test_ptr_with_object(void);
void test_uvector_empty(void);
void test_uvector_with_records(void);
void test_uvector_bool(void);
void test_vector_empty(void);
void test_pvector_empty(void);
void test_pvector_with_records(void);
void data_accessor_column_name(void);
void test_accessor_column_name(gconstpointer data);
void data_accessor_dynamic_pseudo_column_name(void);
void test_accessor_dynamic_pseudo_column_name(gconstpointer data);
void test_column_fix_size(void);
void test_column_var_size(void);
void test_column_index(void);
void test_type(void);
void test_record(void);
void test_proc_command(void);
void test_proc_function(void);

static gchar *tmp_directory;

static grn_ctx *context;
static grn_obj *database;

static grn_obj *inspected;

static grn_obj *void_value;
static grn_obj *int8, *int16, *int32, *int64;
static grn_obj *uint8, *uint16, *uint32, *uint64;
static grn_obj *float_value;
static grn_obj *time_value;
static grn_obj *bool_value;
static grn_obj *text;
static grn_obj *geo_point_tokyo, *geo_point_wgs84;
static grn_obj *ptr;
static grn_obj *uvector;
static grn_obj *pvector;
static grn_obj *vector;
static grn_obj *record;
static grn_table_cursor *cursor;

void
cut_startup(void)
{
  tmp_directory = g_build_filename(grn_test_get_tmp_dir(),
                                   "inspect",
                                   NULL);
}

void
cut_shutdown(void)
{
  g_free(tmp_directory);
}

static void
remove_tmp_directory(void)
{
  cut_remove_path(tmp_directory, NULL);
}

static void
setup_values(void)
{
  void_value = NULL;
  int8 = int16 = int32 = int64 = NULL;
  uint8 = uint16 = uint32 = uint64 = NULL;
  float_value = NULL;
  time_value = NULL;
  bool_value = NULL;
  text = NULL;
  geo_point_tokyo = geo_point_wgs84 = NULL;
  ptr = NULL;
  uvector = NULL;
  pvector = NULL;
  vector = NULL;
  record = NULL;
  cursor = NULL;
}

void
cut_setup(void)
{
  const gchar *database_path;

  inspected = NULL;
  setup_values();

  remove_tmp_directory();
  g_mkdir_with_parents(tmp_directory, 0700);

  context = g_new0(grn_ctx, 1);
  grn_ctx_init(context, 0);

  database_path = cut_build_path(tmp_directory, "database.groonga", NULL);
  database = grn_db_create(context, database_path, NULL);
}

static void
teardown_values(void)
{
  grn_obj_unlink(context, void_value);
  grn_obj_unlink(context, int8);
  grn_obj_unlink(context, int16);
  grn_obj_unlink(context, int32);
  grn_obj_unlink(context, int64);
  grn_obj_unlink(context, uint8);
  grn_obj_unlink(context, uint16);
  grn_obj_unlink(context, uint32);
  grn_obj_unlink(context, uint64);
  grn_obj_unlink(context, float_value);
  grn_obj_unlink(context, time_value);
  grn_obj_unlink(context, bool_value);
  grn_obj_unlink(context, text);
  grn_obj_unlink(context, geo_point_tokyo);
  grn_obj_unlink(context, geo_point_wgs84);
  grn_obj_unlink(context, ptr);
  grn_obj_unlink(context, uvector);
  grn_obj_unlink(context, pvector);
  grn_obj_unlink(context, vector);
  grn_obj_unlink(context, record);
  grn_table_cursor_close(context, cursor);
}

void
cut_teardown(void)
{
  teardown_values();

  grn_obj_unlink(context, inspected);

  grn_obj_close(context, database);
  grn_ctx_fin(context);
  g_free(context);

  remove_tmp_directory();
}

static const gchar *
inspected_string (void)
{
  return cut_take_printf("%.*s",
                         (int)GRN_TEXT_LEN(inspected),
                         GRN_TEXT_VALUE(inspected));
}

void
test_null(void)
{
  inspected = grn_inspect(context, NULL, NULL);
  cut_assert_equal_string("(NULL)", inspected_string());
}

void
test_void_value(void)
{
  void_value = grn_obj_open(context, GRN_BULK, 0, GRN_DB_VOID);
  GRN_TEXT_PUTS(context, void_value, "void");
  inspected = grn_inspect(context, NULL, void_value);
  cut_assert_equal_string("\"void\"", inspected_string());
}

void
test_int8(void)
{
  int8 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_INT8);
  GRN_INT8_SET(context, int8, G_MAXINT8);
  inspected = grn_inspect(context, NULL, int8);
  cut_assert_equal_string(cut_take_printf("%d", G_MAXINT8),
                          inspected_string());
}

void
test_int16(void)
{
  int16 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_INT16);
  GRN_INT16_SET(context, int16, G_MAXINT16);
  inspected = grn_inspect(context, NULL, int16);
  cut_assert_equal_string(cut_take_printf("%" G_GINT16_FORMAT, G_MAXINT16),
                          inspected_string());
}

void
test_int32(void)
{
  int32 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_INT32);
  GRN_INT32_SET(context, int32, G_MAXINT32);
  inspected = grn_inspect(context, NULL, int32);
  cut_assert_equal_string(cut_take_printf("%" G_GINT32_FORMAT, G_MAXINT32),
                          inspected_string());
}

void
test_int64(void)
{
  int64 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_INT64);
  GRN_INT64_SET(context, int64, G_MAXINT64);
  inspected = grn_inspect(context, NULL, int64);
  cut_assert_equal_string(cut_take_printf("%" G_GINT64_FORMAT, G_MAXINT64),
                          inspected_string());
}

void
test_uint8(void)
{
  uint8 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_UINT8);
  GRN_UINT8_SET(context, uint8, G_MAXUINT8);
  inspected = grn_inspect(context, NULL, uint8);
  cut_assert_equal_string(cut_take_printf("%u", G_MAXUINT8),
                          inspected_string());
}

void
test_uint16(void)
{
  uint16 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_UINT16);
  GRN_UINT16_SET(context, uint16, G_MAXUINT16);
  inspected = grn_inspect(context, NULL, uint16);
  cut_assert_equal_string(cut_take_printf("%" G_GUINT16_FORMAT, G_MAXUINT16),
                          inspected_string());
}

void
test_uint32(void)
{
  uint32 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_UINT32);
  GRN_UINT32_SET(context, uint32, G_MAXUINT32);
  inspected = grn_inspect(context, NULL, uint32);
  cut_assert_equal_string(cut_take_printf("%" G_GUINT32_FORMAT, G_MAXUINT32),
                          inspected_string());
}

void
test_uint64(void)
{
  uint64 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_UINT64);
  GRN_UINT64_SET(context, uint64, G_MAXUINT64);
  inspected = grn_inspect(context, NULL, uint64);
  cut_assert_equal_string(cut_take_printf("%" G_GUINT64_FORMAT, G_MAXUINT64),
                          inspected_string());
}

void
test_float(void)
{
  float_value = grn_obj_open(context, GRN_BULK, 0, GRN_DB_FLOAT);
  GRN_FLOAT_SET(context, float_value, 0.29);
  inspected = grn_inspect(context, NULL, float_value);
  cut_assert_equal_string("0.29", inspected_string());
}

void
test_time(void)
{
  GTimeVal g_time_value;

  g_time_val_from_iso8601("2010-05-31T11:50:29.29+0900", &g_time_value);
  time_value = grn_obj_open(context, GRN_BULK, 0, GRN_DB_TIME);
  GRN_TIME_SET(context, time_value,
               (gint64)g_time_value.tv_sec * G_USEC_PER_SEC +
               g_time_value.tv_usec);
  inspected = grn_inspect(context, NULL, time_value);
  cut_assert_equal_string(cut_take_printf("%ld.290000", g_time_value.tv_sec),
                          inspected_string());
}

void
test_bool_true(void)
{
  bool_value = grn_obj_open(context, GRN_BULK, 0, GRN_DB_BOOL);
  GRN_BOOL_SET(context, bool_value, GRN_TRUE);
  inspected = grn_inspect(context, NULL, bool_value);
  cut_assert_equal_string("true", inspected_string());
}

void
test_bool_false(void)
{
  bool_value = grn_obj_open(context, GRN_BULK, 0, GRN_DB_BOOL);
  GRN_BOOL_SET(context, bool_value, GRN_FALSE);
  inspected = grn_inspect(context, NULL, bool_value);
  cut_assert_equal_string("false", inspected_string());
}

void
test_text(void)
{
  text = grn_obj_open(context, GRN_BULK, 0, GRN_DB_TEXT);
  GRN_TEXT_PUTS(context, text, "niku");
  inspected = grn_inspect(context, NULL, text);
  cut_assert_equal_string("\"niku\"", inspected_string());
}

void
test_geo_point_tokyo(void)
{
  gint takane_latitude, takane_longitude;

  takane_latitude = grn_test_coordinate_in_milliseconds(35.6954581363924);
  takane_longitude = grn_test_coordinate_in_milliseconds(139.564207350021);

  geo_point_tokyo = grn_obj_open(context, GRN_BULK, 0, GRN_DB_TOKYO_GEO_POINT);
  GRN_GEO_POINT_SET(context, geo_point_tokyo, takane_latitude, takane_longitude);
  inspected = grn_inspect(context, NULL, geo_point_tokyo);
  cut_assert_equal_string("[(128503649,502431146) "
                          "((35, 41, 43, 649),(139, 33, 51, 146)) "
                          "[00000001 01111011 11011101 10000100 "
                          "10110101 11111011 01101100 01000110]]",
                          inspected_string());
}

void
test_geo_point_wgs84(void)
{
  gint takane_latitude, takane_longitude;

  takane_latitude = grn_test_coordinate_in_milliseconds(35.6986901);
  takane_longitude = grn_test_coordinate_in_milliseconds(139.56099);

  geo_point_wgs84 = grn_obj_open(context, GRN_BULK, 0, GRN_DB_WGS84_GEO_POINT);
  GRN_GEO_POINT_SET(context, geo_point_wgs84, takane_latitude, takane_longitude);
  inspected = grn_inspect(context, NULL, geo_point_wgs84);
  cut_assert_equal_string("[(128515284,502419564) "
                          "((35, 41, 55, 284),(139, 33, 39, 564)) "
                          "[00000001 01111011 11011101 10000100 "
                          "10111011 10100000 10110110 01110000]]",
                          inspected_string());
}

void
test_array_empty(void)
{
  assert_send_command("table_create Sites TABLE_NO_KEY");
  inspected = grn_inspect(context, NULL, get("Sites"));
  cut_assert_equal_string("#<table:no_key "
                          "Sites "
                          "value:(nil) "
                          "size:0 "
                          "columns:[] "
                          "ids:[] "
                          "subrec:none"
                          ">",
                          inspected_string());
}

void
test_array_with_records(void)
{
  assert_send_command("table_create Sites TABLE_NO_KEY");
  assert_send_command("column_create Sites name COLUMN_SCALAR Text");
  assert_send_command("load "
                      "'[[\"name\"],[\"groonga.org\"],[\"razil.jp\"]]' "
                      "Sites");
  inspected = grn_inspect(context, NULL, get("Sites"));
  cut_assert_equal_string("#<table:no_key "
                          "Sites "
                          "value:(nil) "
                          "size:2 "
                          "columns:[name] "
                          "ids:[1, 2] "
                          "subrec:none"
                          ">",
                          inspected_string());
}

void
test_hash_empty(void)
{
  assert_send_command("table_create Sites TABLE_HASH_KEY ShortText");
  inspected = grn_inspect(context, NULL, get("Sites"));
  cut_assert_equal_string("#<table:hash "
                          "Sites "
                          "key:ShortText "
                          "value:(nil) "
                          "size:0 "
                          "columns:[] "
                          "default_tokenizer:(nil) "
                          "normalizers:(nil) "
                          "keys:[] "
                          "subrec:none"
                          ">",
                          inspected_string());
}

void
test_hash_with_records(void)
{
  assert_send_command("table_create Sites TABLE_HASH_KEY ShortText");
  assert_send_command("column_create Sites name COLUMN_SCALAR Text");
  assert_send_command("load "
                      "'["
                      "[\"_key\",\"name\"],"
                      "[\"groonga.org\",\"groonga\"],"
                      "[\"razil.jp\",\"Brazil\"]"
                      "]' "
                      "Sites");
  inspected = grn_inspect(context, NULL, get("Sites"));
  cut_assert_equal_string("#<table:hash "
                          "Sites "
                          "key:ShortText "
                          "value:(nil) "
                          "size:2 "
                          "columns:[name] "
                          "default_tokenizer:(nil) "
                          "normalizers:(nil) "
                          "keys:[\"groonga.org\", \"razil.jp\"] "
                          "subrec:none"
                          ">",
                          inspected_string());
}

void
test_patricia_trie_empty(void)
{
  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  inspected = grn_inspect(context, NULL, get("Sites"));
  cut_assert_equal_string("#<table:pat "
                          "Sites "
                          "key:ShortText "
                          "value:(nil) "
                          "size:0 "
                          "columns:[] "
                          "default_tokenizer:(nil) "
                          "normalizers:(nil) "
                          "keys:[] "
                          "subrec:none "
                          "nodes:{}"
                          ">",
                          inspected_string());
}

void
test_patricia_trie_with_records(void)
{
  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  assert_send_command("column_create Sites name COLUMN_SCALAR Text");
  assert_send_command("load "
                      "'["
                      "[\"_key\",\"name\"],"
                      "[\"groonga.org\",\"groonga\"],"
                      "[\"razil.jp\",\"Brazil\"]"
                      "]' "
                      "Sites");
  inspected = grn_inspect(context, NULL, get("Sites"));
  cut_assert_equal_string(
    "#<table:pat "
    "Sites "
    "key:ShortText "
    "value:(nil) "
    "size:2 "
    "columns:[name] "
    "default_tokenizer:(nil) "
    "normalizers:(nil) "
    "keys:[\"groonga.org\", \"razil.jp\"] "
    "subrec:none "
    "nodes:{\n"
    "2{0,3,false}\n"
    "  L:1{10,7,false}\n"
    "    L:0{0,0,false}\n"
    "    R:1{10,7,false}(\"groonga.org\")[01100111 01110010 01101111 01101111 "
                                         "01101110 01100111 01100001 00101110 "
                                         "01101111 01110010 01100111]\n"
    "  R:2{0,3,false}(\"razil.jp\")[01110010 01100001 01111010 01101001 "
                                   "01101100 00101110 01101010 01110000]\n"
    "}"
    ">",
    inspected_string());
}

void
test_patricia_trie_cursor_empty(void)
{
  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  cursor = grn_table_cursor_open(context, get("Sites"),
                                 NULL, 0,
                                 NULL, 0,
                                 0, -1, GRN_CURSOR_ASCENDING);
  inspected = grn_inspect(context, NULL, cursor);
  cut_assert_equal_string("#<cursor:pat:Sites "
                          "current:0 "
                          "tail:0 "
                          "flags:ascending|greater|less "
                          "rest:0 "
                          "entries:[]"
                          ">",
                          inspected_string());
}

void
test_patricia_trie_cursor_with_records(void)
{
  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  assert_send_command("column_create Sites name COLUMN_SCALAR Text");
  assert_send_command("load "
                      "'["
                      "[\"_key\",\"name\"],"
                      "[\"groonga.org\",\"groonga\"],"
                      "[\"razil.jp\",\"Brazil\"]"
                      "]' "
                      "Sites");
  cursor = grn_table_cursor_open(context, get("Sites"),
                                 NULL, 0,
                                 NULL, 0,
                                 0, -1, GRN_CURSOR_ASCENDING);
  inspected = grn_inspect(context, NULL, cursor);
  cut_assert_equal_string("#<cursor:pat:Sites "
                          "current:0 "
                          "tail:0 "
                          "flags:ascending|greater|less "
                          "rest:2 "
                          "entries:[[2,{0,3,false}], [1,{0,3,false}]]"
                          ">",
                          inspected_string());
}

void
test_ptr_empty(void)
{
  ptr = grn_obj_open(context, GRN_PTR, 0, GRN_ID_NIL);
  inspected = grn_inspect(context, NULL, ptr);
  cut_assert_equal_string("#<ptr:(empty)>", inspected_string());
}

void
test_ptr_with_object(void)
{
  text = grn_obj_open(context, GRN_BULK, 0, GRN_DB_TEXT);
  GRN_TEXT_PUTS(context, text, "niku");

  ptr = grn_obj_open(context, GRN_PTR, 0, GRN_ID_NIL);
  GRN_PTR_SET(context, ptr, text);
  inspected = grn_inspect(context, NULL, ptr);
  cut_assert_equal_string("#<ptr:\"niku\">", inspected_string());
}

void
test_uvector_empty(void)
{
  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  uvector = grn_obj_open(context, GRN_UVECTOR, 0,
                         grn_obj_id(context, get("Sites")));
  inspected = grn_inspect(context, NULL, uvector);
  cut_assert_equal_string("[]", inspected_string());
}

void
test_uvector_with_records(void)
{
  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  assert_send_command("load "
                      "'[[\"_key\"],[\"groonga.org\"],[\"razil.jp\"]]' "
                      "Sites");
  uvector = grn_obj_open(context, GRN_UVECTOR, 0,
                         grn_obj_id(context, get("Sites")));
  GRN_RECORD_PUT(context, uvector, 1);
  GRN_RECORD_PUT(context, uvector, 2);
  inspected = grn_inspect(context, NULL, uvector);
  cut_assert_equal_string(
    "["
    "#<element record:#<record:pat:Sites id:1 key:\"groonga.org\">, weight:0.000000>, "
    "#<element record:#<record:pat:Sites id:2 key:\"razil.jp\">, weight:0.000000>"
    "]",
    inspected_string());
}

void
test_uvector_bool(void)
{
  uvector = grn_obj_open(context, GRN_UVECTOR, 0, GRN_DB_BOOL);
  GRN_BOOL_PUT(context, uvector, TRUE);
  GRN_BOOL_PUT(context, uvector, FALSE);
  inspected = grn_inspect(context, NULL, uvector);
  cut_assert_equal_string("[true,false]", inspected_string());
}

void
test_pvector_empty(void)
{
  pvector = grn_obj_open(context, GRN_PVECTOR, 0, GRN_ID_NIL);
  inspected = grn_inspect(context, NULL, pvector);
  cut_assert_equal_string("[]", inspected_string());
}

void
test_pvector_with_records(void)
{
  grn_obj *groonga, *razil;

  pvector = grn_obj_open(context, GRN_PVECTOR, 0, GRN_ID_NIL);
  groonga = grn_obj_open(context, GRN_BULK, 0, GRN_DB_SHORT_TEXT);
  razil = grn_obj_open(context, GRN_BULK, 0, GRN_DB_SHORT_TEXT);
  GRN_TEXT_PUTS(context, groonga, "groonga");
  GRN_TEXT_PUTS(context, razil, "razil");
  GRN_PTR_PUT(context, pvector, groonga);
  GRN_PTR_PUT(context, pvector, razil);
  inspected = grn_inspect(context, NULL, pvector);
  grn_obj_unlink(context, groonga);
  grn_obj_unlink(context, razil);
  cut_assert_equal_string("[\"groonga\", \"razil\"]", inspected_string());
}

void
test_vector_empty(void)
{
  vector = grn_obj_open(context, GRN_VECTOR, 0, GRN_DB_TEXT);
  inspected = grn_inspect(context, NULL, vector);
  cut_assert_equal_string("[]", inspected_string());
}

void
data_accessor_column_name(void)
{
#define ADD_DATUM(table, accessor, inspected)           \
  gcut_add_datum(table "." accessor,                    \
                 "table", G_TYPE_STRING, table,         \
                 "accessor", G_TYPE_STRING, accessor,   \
                 "inspected", G_TYPE_STRING, inspected, \
                 NULL)

  ADD_DATUM("Sites",
            "_id",
            "#<accessor _id(Sites)>");
  ADD_DATUM("Sites",
            "_key",
            "#<accessor _key(Sites)>");
  ADD_DATUM("Sites",
            "_value",
            "#<accessor _value(Sites)>");
  ADD_DATUM("Sites",
            "name._id",
            "#<accessor name(Sites)._id(Names)>");
  ADD_DATUM("Sites",
            "name._key",
            "#<accessor name(Sites)._key(Names)>");
  ADD_DATUM("Sites",
            "name._value",
            "#<accessor name(Sites)._value(Names)>");
  ADD_DATUM("Sites",
            "name.site",
            "#<accessor name(Sites).site(Names)>");
  ADD_DATUM("Sites",
            "name.site.name",
            "#<accessor name(Sites).site(Names).name(Sites)>");
  ADD_DATUM("Names",
            "site.name.site",
            "#<accessor site(Names).name(Sites).site(Names)>");

#undef ADD_DATUM
}

void
test_accessor_column_name(gconstpointer data)
{
  const char *table_name = gcut_data_get_string(data, "table");
  const char *accessor_name = gcut_data_get_string(data, "accessor");
  const char *inspected_accessor = gcut_data_get_string(data, "inspected");
  grn_obj *object, *accessor;

  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText Int32");
  assert_send_command("table_create Names TABLE_PAT_KEY ShortText UInt32");
  assert_send_command("column_create Sites name COLUMN_SCALAR Names");
  assert_send_command("column_create Names site COLUMN_SCALAR Sites");

  object = get_object(table_name);
  accessor = grn_obj_column(context, object,
                            accessor_name, strlen(accessor_name));
  grn_obj_unlink(context, object);
  cut_assert_not_null(accessor);
  inspected = grn_inspect(context, NULL, accessor);
  grn_obj_unlink(context, accessor);
  cut_assert_equal_string(inspected_accessor, inspected_string());
}

void
data_accessor_dynamic_pseudo_column_name(void)
{
#define ADD_DATUM(accessor, inspected)                  \
  gcut_add_datum(accessor,                              \
                 "accessor", G_TYPE_STRING, accessor,   \
                 "inspected", G_TYPE_STRING, inspected, \
                 NULL)

  ADD_DATUM("_score", "#<accessor _score>");

#undef ADD_DATUM
}

void
test_accessor_dynamic_pseudo_column_name(gconstpointer data)
{
  const char *query, *accessor_name, *inspected_accessor;
  grn_obj *table, *result, *expression, *variable, *accessor;

  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  assert_send_command("column_create Sites uri COLUMN_SCALAR ShortText");
  assert_send_command("load "
                      "'["
                      "[\"_key\",\"uri\"],"
                      "[\"groonga\",\"http://groonga.org/\"]"
                      "]' "
                      "Sites");

  accessor_name = gcut_data_get_string(data, "accessor");
  inspected_accessor = gcut_data_get_string(data, "inspected");

  table = get_object("Sites");
  GRN_EXPR_CREATE_FOR_QUERY(context, table, expression, variable);
  query = "_key:groonga";
  grn_expr_parse(context, expression,
                 query, strlen(query),
                 NULL, GRN_OP_MATCH, GRN_OP_AND,
                 GRN_EXPR_SYNTAX_QUERY | GRN_EXPR_ALLOW_COLUMN);
  result = grn_table_select(context, table, expression, NULL, GRN_OP_AND);
  accessor = grn_obj_column(context, result,
                            accessor_name, strlen(accessor_name));
  cut_assert_not_null(accessor);
  inspected = grn_inspect(context, NULL, accessor);
  grn_obj_unlink(context, accessor);
  cut_assert_equal_string(inspected_accessor, inspected_string());
}

void
test_column_fix_size(void)
{
  grn_obj *column;

  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  assert_send_command("column_create Sites score COLUMN_SCALAR Int32");

  column = get_object("Sites.score");
  inspected = grn_inspect(context, NULL, column);
  cut_assert_equal_string("#<column:fix_size "
                          "Sites.score "
                          "range:Int32 "
                          "type:scalar "
                          "compress:none"
                          ">",
                          inspected_string());
}

void
test_column_var_size(void)
{
  grn_obj *column;

  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  assert_send_command("column_create Sites name COLUMN_SCALAR ShortText");

  column = get_object("Sites.name");
  inspected = grn_inspect(context, NULL, column);
  cut_assert_equal_string("#<column:var_size "
                          "Sites.name "
                          "range:ShortText "
                          "type:scalar "
                          "compress:none"
                          ">",
                          inspected_string());
}

void
test_column_index(void)
{
  grn_obj *column;

  assert_send_command("table_create Sites TABLE_PAT_KEY ShortText");
  assert_send_command("table_create Terms TABLE_PAT_KEY ShortText");
  assert_send_command("column_create Terms Sites_key COLUMN_INDEX Sites _key");

  column = get_object("Terms.Sites_key");
  inspected = grn_inspect(context, NULL, column);
  cut_assert_equal_string("#<column:index "
                          "Terms.Sites_key "
                          "range:Sites "
                          "sources:[Sites] "
                          "flags:NONE>",
                          inspected_string());
}

void
test_type(void)
{
  grn_obj *type;

  type = get_object("ShortText");
  inspected = grn_inspect(context, NULL, type);
  cut_assert_equal_string("#<type "
                          "ShortText "
                          "size:4096 "
                          "type:var_size"
                          ">",
                          inspected_string());
}

void
test_record(void)
{
  assert_send_command("table_create Sites TABLE_HASH_KEY ShortText");
  assert_send_command("column_create Sites name COLUMN_SCALAR Text");
  assert_send_command("load "
                      "'["
                      "[\"_key\",\"name\"],"
                      "[\"groonga.org\",\"groonga\"],"
                      "[\"razil.jp\",\"Brazil\"]"
                      "]' "
                      "Sites");

  record = grn_obj_open(context, GRN_BULK, 0,
                        grn_obj_id(context, get_object("Sites")));
  GRN_RECORD_SET(context, record, 1);
  inspected = grn_inspect(context, NULL, record);
  cut_assert_equal_string("#<record:hash:Sites "
                          "id:1 "
                          "key:\"groonga.org\" "
                          "name:\"groonga\""
                          ">",
                          inspected_string());
}

void
test_proc_command(void)
{
  grn_obj *proc;

  proc = get_object("column_remove");
  inspected = grn_inspect(context, NULL, proc);
  cut_assert_equal_string("#<proc:command "
                          "column_remove "
                          "arguments:[table, name]"
                          ">",
                          inspected_string());
}

void
test_proc_function(void)
{
  grn_obj *proc;

  proc = get_object("geo_distance");
  inspected = grn_inspect(context, NULL, proc);
  cut_assert_equal_string("#<proc:function "
                          "geo_distance "
                          "arguments:[]"
                          ">",
                          inspected_string());
}
