static char* test_api_assertion()
{
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/counter.lua", "", NULL);
  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s", ret);

  lsb_stop_sandbox(NULL);
  mu_assert(lsb_destroy(NULL) == NULL, "not null");
  mu_assert(lsb_usage(NULL, 0, 0) == 0, "not 0");
  mu_assert(lsb_usage(sb, LSB_UT_MAX, 0) == 0, "not 0");
  mu_assert(lsb_usage(sb, 0, LSB_US_MAX) == 0, "not 0");
  mu_assert(strcmp(lsb_get_error(NULL), "") == 0, "not empty");
  lsb_set_error(NULL, "foo");
  mu_assert(lsb_get_lua(NULL) == NULL, "not null");
  mu_assert(lsb_get_lua_file(NULL) == NULL, "not null");
  mu_assert(lsb_get_parent(NULL) == NULL, "not null");
  mu_assert(lsb_get_logger(NULL) == NULL, "not null");
  mu_assert(lsb_get_state(NULL) == LSB_UNKNOWN, "not unknown");
  lsb_add_function(NULL, lsb_test_write_output, "foo");
  lsb_add_function(sb, NULL, "foo");
  lsb_add_function(sb, lsb_test_write_output, NULL);
  mu_assert(lsb_pcall_setup(NULL, "foo") == LSB_ERR_UTIL_NULL, "not null");
  mu_assert(lsb_pcall_setup(sb, NULL) == LSB_ERR_UTIL_NULL, "not null");
  lsb_add_function(NULL, NULL, NULL);
  lsb_pcall_teardown(NULL);
  lsb_terminate(NULL, NULL);
  lsb_terminate(sb, NULL);
  lsb_add_function(sb, lsb_test_write_output, "write_output");

  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);
  return NULL;
}
static char* test_init_error()
{
  // null sandbox
  int result = lsb_init(NULL, NULL);
  mu_assert(result == 0, "lsb_init() null sandbox ptr");

  // load error
  lua_sandbox* sb = lsb_create(NULL, "lua/simple1.lua", 65765, 1000,
                               1024);
  mu_assert(sb, "lsb_create() received: NULL");
  result = lsb_init(sb, NULL);
  mu_assert(result == 2, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));
  lsb_state s = lsb_get_state(sb);
  mu_assert(s == LSB_TERMINATED, "lsb_get_state() received: %d", s);
  e = lsb_destroy(sb, NULL);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  // out of memory
  sb = lsb_create(NULL, "lua/simple.lua", 6000, 1000, 1024);
  mu_assert(sb, "lsb_create() received: NULL");
  result = lsb_init(sb, NULL);
  mu_assert(result == 2, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));
  e = lsb_destroy(sb, NULL);
  mu_assert(!e, "lsb_destroy() received: %s\n", e);

  return NULL;
}
static char* test_restore()
{
  const char *output_file = "restore.preserve";

  remove(output_file);
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/restore.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  lsb_err_value ret = lsb_init(sb, output_file);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  lsb_add_function(sb, &lsb_test_write_output, "write_output");
  int result = lsb_test_process(sb, 0);
  mu_assert(result == 0, "process() received: %d %s", result,
            lsb_get_error(sb));
  mu_assert(strcmp("101", lsb_test_output) == 0, "test: initial load received: %s",
            lsb_test_output);
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  // re-load to test the preserved data
  sb = lsb_create(NULL, "lua/restore.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  ret = lsb_init(sb, output_file);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  lsb_add_function(sb, &lsb_test_write_output, "write_output");
  result = lsb_test_process(sb, 0);
  mu_assert(result == 0, "process() received: %d %s", result,
            lsb_get_error(sb));
  mu_assert(strcmp("102", lsb_test_output) == 0, "test: reload received: %s",
            lsb_test_output);
  result = lsb_test_report(sb, 2); // change the preservation version
  mu_assert(result == 0, "report() received: %d", result);
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  // re-load to test the preserved data with a version change
  sb = lsb_create(NULL, "lua/restore.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  ret = lsb_init(sb, output_file);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  lsb_add_function(sb, &lsb_test_write_output, "write_output");
  result = lsb_test_process(sb, 0);
  mu_assert(result == 0, "process() received: %d %s", result,
            lsb_get_error(sb));
  mu_assert(strcmp("101", lsb_test_output) == 0,
            "test: reload with version change received: %s", lsb_test_output);
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_create()
{
  static char *cfg = "function foo() return 0 end\nt = {[true] = 1}\n";
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/counter.lua", cfg, &lsb_test_logger);
  mu_assert(sb, "lsb_create() failed");
  lsb_destroy(sb);
  sb = lsb_create(NULL, "lua/counter.lua", cfg, NULL);
  mu_assert(sb, "lsb_create() failed");
  lsb_destroy(sb);
  sb = lsb_create(NULL, "lua/counter.lua", "memory_limit = 3e9", NULL);
  mu_assert(sb, "lsb_create() failed");
  lsb_destroy(sb);
  return NULL;
}
static char* test_print_lsb_test_logger()
{
  const char *tests[] =
  {
    ""
    , "7 test.print foo \t10\ttrue"
    , NULL
  };
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/print.lua", "log_level = 7;Logger = 'test.print';", &printer);
  mu_assert(sb, "lsb_create() received: NULL");

  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s", ret);

  for (int i = 0; tests[i]; ++i) {
    print_out[0] = 0;
    int result = lsb_test_process(sb, i);
    mu_assert(result == 0, "test: %d received: %d error: %s", i, result, lsb_get_error(sb));
    mu_assert(strcmp(tests[i], print_out) == 0, "test: %d expected: %s received: %s", i, tests[i], print_out);
  }

  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);
  return NULL;
}
static char* test_usage_error()
{
  unsigned u = lsb_usage(NULL, LSB_UT_MEMORY, LSB_US_CURRENT);
  mu_assert(u == 0, "NULL sandbox memory usage received: %u", u);

  lua_sandbox* sb = lsb_create(NULL, "lua/simple.lua", 65765, 1000,
                               1024);
  mu_assert(sb, "lsb_create() received: NULL");

  u = lsb_usage(NULL, LSB_UT_MAX + 1, LSB_US_CURRENT);
  mu_assert(u == 0, "Invalid usage type received: %u", u);

  u = lsb_usage(NULL, LSB_UT_MEMORY, LSB_US_MAX + 1);
  mu_assert(u == 0, "Invalid usage stat received: %u", u);

  mu_assert(sb, "lsb_create() received: NULL");
  lsb_terminate(sb, "forced termination");
  u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_CURRENT);
  mu_assert(u == 0, "Terminated memory usage received: %u", u);

  e = lsb_destroy(sb, NULL);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_serialize()
{
  const char* output_file = "serialize.preserve";
  lua_sandbox* sb = lsb_create(NULL, "lua/serialize.lua", 64000, 1000,
                               64000);
  mu_assert(sb, "lsb_create() received: NULL");

  int result = lsb_init(sb, NULL);
  mu_assert(result == 0, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));
  e = lsb_destroy(sb, output_file);
  mu_assert(!e, "lsb_destroy() received: %s", e);

#ifdef LUA_JIT
  char* expected = read_file("output/serialize.data");
#else
  char* expected = read_file("output/serialize.lua51.data");
#endif
  char* actual = read_file(output_file);
  mu_assert(strcmp(expected, actual) == 0, "serialization mismatch");
  free(expected);
  free(actual);

  return NULL;
}
static char* test_output()
{
  const char *outputs[] = {
    "1.2 string nil true false",
    "foo",
    NULL
  };

  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/output.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  add_ud_module(sb);

  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  lsb_add_function(sb, &lsb_test_write_output, "write_output");

  for (int x = 0; outputs[x]; ++x) {
    int result = lsb_test_process(sb, x);
    mu_assert(!result, "process() test: %d failed: %d %s", x, result,
              lsb_get_error(sb));
    if (outputs[x][0]) {
      mu_assert(strcmp(outputs[x], lsb_test_output) == 0,
                "test: %d received: %s", x, lsb_test_output);
    }
  }

  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_simple_error()
{
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/simple.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");

  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s", ret);

  int result = lsb_test_process(sb, 1);
  mu_assert(result == 0, "process() received: %d %s", result,
            lsb_get_error(sb));

  mu_assert(strcmp("ok", lsb_get_error(sb)) == 0, "process() received: %d %s", result,
            lsb_get_error(sb));

  result = lsb_test_process(sb, 0);
  mu_assert(result == 0, "process() received: %d %s", result,
            lsb_get_error(sb));

  mu_assert(strcmp("", lsb_get_error(sb)) == 0, "process() received: %d %s", result,
            lsb_get_error(sb));

  result = lsb_test_process(sb, 2);
  mu_assert(result == 1, "process() received: %d %s", result,
            lsb_get_error(sb));

  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_usage_error()
{
  size_t u = lsb_usage(NULL, LSB_UT_MEMORY, LSB_US_CURRENT);
  mu_assert(u == 0, "NULL sandbox memory usage received: %" PRIuSIZE, u);

  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/simple.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");

  u = lsb_usage(NULL, LSB_UT_MAX + 1, LSB_US_CURRENT);
  mu_assert(u == 0, "Invalid usage type received: %" PRIuSIZE, u);

  u = lsb_usage(NULL, LSB_UT_MEMORY, LSB_US_MAX + 1);
  mu_assert(u == 0, "Invalid usage stat received: %" PRIuSIZE, u);

  mu_assert(sb, "lsb_create() received: NULL");
  lsb_terminate(sb, "forced termination");
  lsb_state s = lsb_get_state(sb);
  mu_assert(s == LSB_TERMINATED, "lsb_get_state() received: %d", s);
  u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_CURRENT);
  mu_assert(u > 0, "Terminated memory usage received: 0");

  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_output_errors()
{
  const char *tests[] =
  {
    "process() lua/output_errors.lua:10: bad argument #1 to 'output' (unsupported type)"
    , "process() lua/output_errors.lua:16: output_limit exceeded"
    , "process() lua/output_errors.lua:18: bad argument #1 to 'write_output' (unknown userdata type)"
    , NULL
  };

  for (int i = 0; tests[i]; ++i) {
    lsb_lua_sandbox *sb = lsb_create(NULL, "lua/output_errors.lua",
                                     MODULE_PATH "output_limit = 128", NULL);
    mu_assert(sb, "lsb_create() received: NULL");

    lsb_err_value ret = lsb_init(sb, NULL);
    mu_assert(!ret, "lsb_init() received: %s", ret);
    lsb_add_function(sb, &lsb_test_write_output, "write_output");

    int result = lsb_test_process(sb, i);
    mu_assert(result == 1, "test: %d received: %d", i, result);

    const char *le = lsb_get_error(sb);
    mu_assert(le, "test: %d received NULL", i);
    mu_assert(strcmp(tests[i], le) == 0, "test: %d received: %s", i, le);

    e = lsb_destroy(sb);
    mu_assert(!e, "lsb_destroy() received: %s", e);
  }

  return NULL;
}
static char* test_cbuf()
{
  const char* outputs[] = {
    "{\"time\":0,\"rows\":3,\"columns\":3,\"seconds_per_row\":1,\"column_info\":[{\"name\":\"Add_column\",\"unit\":\"count\",\"aggregation\":\"sum\"},{\"name\":\"Set_column\",\"unit\":\"count\",\"aggregation\":\"sum\"},{\"name\":\"Get_column\",\"unit\":\"count\",\"aggregation\":\"sum\"}]}\n0\t0\t0\n0\t0\t0\n0\t0\t0\n"
    , "{\"time\":0,\"rows\":3,\"columns\":3,\"seconds_per_row\":1,\"column_info\":[{\"name\":\"Add_column\",\"unit\":\"count\",\"aggregation\":\"sum\"},{\"name\":\"Set_column\",\"unit\":\"count\",\"aggregation\":\"sum\"},{\"name\":\"Get_column\",\"unit\":\"count\",\"aggregation\":\"sum\"}]}\n1\t1\t1\n2\t1\t2\n3\t1\t3\n"
    , "{\"time\":2,\"rows\":3,\"columns\":3,\"seconds_per_row\":1,\"column_info\":[{\"name\":\"Add_column\",\"unit\":\"count\",\"aggregation\":\"sum\"},{\"name\":\"Set_column\",\"unit\":\"count\",\"aggregation\":\"sum\"},{\"name\":\"Get_column\",\"unit\":\"count\",\"aggregation\":\"sum\"}]}\n3\t1\t3\n0\t0\t0\n1\t1\t1\n"
    , "{\"time\":8,\"rows\":3,\"columns\":3,\"seconds_per_row\":1,\"column_info\":[{\"name\":\"Add_column\",\"unit\":\"count\",\"aggregation\":\"sum\"},{\"name\":\"Set_column\",\"unit\":\"count\",\"aggregation\":\"sum\"},{\"name\":\"Get_column\",\"unit\":\"count\",\"aggregation\":\"sum\"}]}\n0\t0\t0\n0\t0\t0\n1\t1\t1\n"
    , NULL
  };

  lua_sandbox* sb = lsb_create(NULL, "lua/circular_buffer.lua", 32767,
                               1000, 32767);
  mu_assert(sb, "lsb_create() received: NULL");

  int result = lsb_init(sb, NULL);
  mu_assert(result == 0, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));
  lsb_add_function(sb, &write_output, "write");

  result = report(sb, 0);
  mu_assert(lsb_get_state(sb) == LSB_RUNNING, "error %s",
            lsb_get_error(sb));
  mu_assert(strcmp(outputs[0], written_data) == 0, "received: %s",
            written_data);

  process(sb, 0);
  process(sb, 1e9);
  process(sb, 1e9);
  process(sb, 2e9);
  process(sb, 2e9);
  process(sb, 2e9);
  result = report(sb, 0);
  mu_assert(result == 0, "report() received: %d", result);
  mu_assert(strcmp(outputs[1], written_data) == 0, "received: %s",
            written_data);

  process(sb, 4e9);
  result = report(sb, 0);
  mu_assert(result == 0, "report() received: %d", result);
  mu_assert(strcmp(outputs[2], written_data) == 0, "received: %s",
            written_data);

  process(sb, 10e9);
  result = report(sb, 0);
  mu_assert(result == 0, "report() received: %d", result);
  mu_assert(strcmp(outputs[3], written_data) == 0, "received: %s",
            written_data);

  result = report(sb, 1);
  mu_assert(result == 0, "report() received: %d", result);

  result = report(sb, 3);
  mu_assert(result == 0, "report() received: %d", result);

  e = lsb_destroy(sb, "circular_buffer.preserve");
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_core()
{
  lsb_lua_sandbox *sb = lsb_create(NULL, "test.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s %s", ret, lsb_get_error(sb));
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);
  return NULL;
}
Exemple #14
0
char* lsb_heka_destroy_sandbox(lsb_heka_sandbox *hsb)
{
  if (!hsb) return NULL;

  char *msg = lsb_destroy(hsb->lsb);
  free(hsb->hostname);
  free(hsb->name);
  free(hsb);
  return msg;
}
static char* test_destroy_error()
{
  const char *expected = "preserve_global_data could not open: "
      "invaliddir/simple.preserve";
  e = lsb_destroy(NULL);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/simple.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  lsb_err_value ret = lsb_init(sb, "invaliddir/simple.preserve");
  mu_assert(!ret, "lsb_init() received: %s", ret);
  e = lsb_destroy(sb);
  mu_assert(e, "lsb_destroy() received NULL");
  mu_assert(strcmp(e, expected) == 0,
            "lsb_destroy() received: %s", e);
  free(e);
  e = NULL;

  return NULL;
}
static char* test_cbuf_errors()
{
  const char* tests[] =
  {
    "process() lua/circular_buffer_errors.lua:9: bad argument #0 to 'new' (incorrect number of arguments)"
    , "process() lua/circular_buffer_errors.lua:11: bad argument #1 to 'new' (number expected, got nil)"
    , "process() lua/circular_buffer_errors.lua:13: bad argument #1 to 'new' (rows must be > 1)"
    , "process() lua/circular_buffer_errors.lua:15: bad argument #2 to 'new' (number expected, got nil)"
    , "process() lua/circular_buffer_errors.lua:17: bad argument #2 to 'new' (columns must be > 0)"
    , "process() lua/circular_buffer_errors.lua:19: bad argument #3 to 'new' (number expected, got nil)"
    , "process() lua/circular_buffer_errors.lua:21: bad argument #3 to 'new' (seconds_per_row is out of range)"
    , "process() lua/circular_buffer_errors.lua:23: bad argument #3 to 'new' (seconds_per_row is out of range)"
    , "process() not enough memory"
    , "process() lua/circular_buffer_errors.lua:28: bad argument #2 to 'set' (column out of range)"
    , "process() lua/circular_buffer_errors.lua:31: bad argument #2 to 'set' (column out of range)"
    , "process() lua/circular_buffer_errors.lua:34: bad argument #2 to 'set' (number expected, got nil)"
    , "process() lua/circular_buffer_errors.lua:37: bad argument #1 to 'set' (number expected, got nil)"
    , "process() lua/circular_buffer_errors.lua:41: bad argument #1 to 'get' (Heka.circular_buffer expected, got number)"
    , "process() lua/circular_buffer_errors.lua:44: bad argument #3 to 'set' (number expected, got nil)"
    , "process() lua/circular_buffer_errors.lua:47: bad argument #-1 to 'set' (incorrect number of arguments)"
    , "process() lua/circular_buffer_errors.lua:50: bad argument #-1 to 'add' (incorrect number of arguments)"
    , "process() lua/circular_buffer_errors.lua:53: bad argument #-1 to 'get' (incorrect number of arguments)"
    , "process() lua/circular_buffer_errors.lua:56: bad argument #-1 to 'compute' (incorrect number of arguments)"
    , "process() lua/circular_buffer_errors.lua:59: bad argument #1 to 'compute' (invalid option 'func')"
    , "process() lua/circular_buffer_errors.lua:62: bad argument #2 to 'compute' (column out of range)"
    , "process() lua/circular_buffer_errors.lua:65: bad argument #4 to 'compute' (end must be >= start)"
    , "process() lua/circular_buffer_errors.lua:68: bad argument #1 to 'format' (invalid option 'invalid')"
    , "process() lua/circular_buffer_errors.lua:71: bad argument #-1 to 'format' (incorrect number of arguments)"
    , "process() lua/circular_buffer_errors.lua:74: bad argument #-1 to 'format' (incorrect number of arguments)"
    , NULL
  };

  for (int i = 0; tests[i]; ++i) {
    lua_sandbox* sb = lsb_create(NULL, "lua/circular_buffer_errors.lua",
                                 32767, 1000, 128);
    mu_assert(sb, "lsb_create() received: NULL");

    int result = lsb_init(sb, NULL);
    mu_assert(result == 0, "lsb_init() received: %d %s", result,
              lsb_get_error(sb));

    result = process(sb, i);
    mu_assert(result == 1, "test: %d received: %d", i, result);

    const char* le = lsb_get_error(sb);
    mu_assert(le, "test: %d received NULL", i);
    mu_assert(strcmp(tests[i], le) == 0, "test: %d received: %s", i, le);

    e = lsb_destroy(sb, NULL);
    mu_assert(!e, "lsb_destroy() received: %s", e);
  }

  return NULL;
}
static char* test_simple()
{
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/simple.lua",
                                   "memory_limit = 65765;"
                                   "instruction_limit = 1000;"
                                   "output_limit = 1024;", NULL);
  mu_assert(sb, "lsb_create() received: NULL");

  lsb_err_value ret = lsb_init(sb, "simple.preserve");
  mu_assert(!ret, "lsb_init() received: %s", ret);

  size_t u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_CURRENT);
  mu_assert(u > 0, "Current memory usage received: %" PRIuSIZE, u);
  printf("cur_mem %" PRIuSIZE "\n", u);

  u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_MAXIMUM);
  mu_assert(u > 0, "Maximum memory usage received: %" PRIuSIZE, u);
  printf("max_mem %" PRIuSIZE "\n", u);

  u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_LIMIT);
  mu_assert(u == 65765, "Memory limit received: %" PRIuSIZE, u);

  u = lsb_usage(sb, LSB_UT_INSTRUCTION, LSB_US_CURRENT);
  mu_assert(u == 7, "Current instructions received: %" PRIuSIZE, u);

  u = lsb_usage(sb, LSB_UT_INSTRUCTION, LSB_US_MAXIMUM);
  mu_assert(u == 7, "Maximum instructions received: %" PRIuSIZE, u);
  printf("max_ins %" PRIuSIZE "\n", u);

  u = lsb_usage(sb, LSB_UT_INSTRUCTION, LSB_US_LIMIT);
  mu_assert(u == 1000, "Instruction limit received: %" PRIuSIZE, u);

  u = lsb_usage(sb, LSB_UT_OUTPUT, LSB_US_CURRENT);
  mu_assert(u == 0, "Current output received: %" PRIuSIZE, u);

  u = lsb_usage(sb, LSB_UT_OUTPUT, LSB_US_MAXIMUM);
  mu_assert(u == 0, "Maximum output received: %" PRIuSIZE, u);
  printf("max_out %" PRIuSIZE "\n", u);

  u = lsb_usage(sb, LSB_UT_OUTPUT, LSB_US_LIMIT);
  mu_assert(u == 1024, "Output limit received: %" PRIuSIZE, u);

  u = lsb_usage(sb, LSB_UT_OUTPUT, LSB_US_LIMIT);
  mu_assert(u == 1024, "Output limit received: %" PRIuSIZE, u);

  lsb_state s = lsb_get_state(sb);
  mu_assert(s == LSB_RUNNING, "lsb_get_state() received: %d", s);

  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_init_error()
{
  // null sandbox
  lsb_err_value ret = lsb_init(NULL, NULL);
  mu_assert(ret == LSB_ERR_UTIL_NULL, "lsb_init() null sandbox ptr");

  // load error
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/simple1.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  ret = lsb_init(sb, NULL);
  mu_assert(ret == LSB_ERR_LUA, "lsb_init() received: %s", lsb_err_string(ret));
  lsb_state s = lsb_get_state(sb);
  mu_assert(s == LSB_TERMINATED, "lsb_get_state() received: %d", s);
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  // out of memory
  sb = lsb_create(NULL, "lua/simple.lua", "memory_limit = 6000", NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  ret = lsb_init(sb, NULL);
  mu_assert(ret == LSB_ERR_LUA, "lsb_init() received: %s", lsb_err_string(ret));
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s\n", e);

  sb = lsb_create(NULL, "lua/no_external_modules.lua", NULL, NULL);
  mu_assert(sb, "lsb_create() received: NULL");

  // disabled external modules
  ret = lsb_init(sb, NULL);
  mu_assert(ret == LSB_ERR_LUA, "lsb_init() received: %s", lsb_err_string(ret));
  const char *expected = "no 'path' configuration was specified for the "
      "sandbox; external modules have been disabled";
  mu_assert(strcmp(lsb_get_error(sb), expected) == 0,
            "lsb_get_error() received: %s", lsb_get_error(sb));

  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_simple()
{
  lua_sandbox* sb = lsb_create(NULL, "lua/simple.lua", 65765, 1000,
                               1024);
  mu_assert(sb, "lsb_create() received: NULL");

  int result = lsb_init(sb, NULL);
  mu_assert(result == 0, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));

  unsigned u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_CURRENT);
  mu_assert(u > 0, "Current memory usage received: %u", u);
  printf("cur_mem %u\n", u);

  u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_MAXIMUM);
  mu_assert(u > 0, "Maximum memory usage received: %u", u);
  printf("max_mem %u\n", u);

  u = lsb_usage(sb, LSB_UT_MEMORY, LSB_US_LIMIT);
  mu_assert(u == 65765, "Memory limit received: %u", u);

  u = lsb_usage(sb, LSB_UT_INSTRUCTION, LSB_US_CURRENT);
  mu_assert(u == 7, "Current instructions received: %u", u);

  u = lsb_usage(sb, LSB_UT_INSTRUCTION, LSB_US_MAXIMUM);
  mu_assert(u == 7, "Maximum instructions received: %u", u);
  printf("max_ins %u\n", u);

  u = lsb_usage(sb, LSB_UT_INSTRUCTION, LSB_US_LIMIT);
  mu_assert(u == 1000, "Instruction limit received: %u", u);

  u = lsb_usage(sb, LSB_UT_OUTPUT, LSB_US_CURRENT);
  mu_assert(u == 0, "Current output received: %u", u);

  u = lsb_usage(sb, LSB_UT_OUTPUT, LSB_US_MAXIMUM);
  mu_assert(u == 0, "Maximum output received: %u", u);
  printf("max_out %u\n", u);

  u = lsb_usage(sb, LSB_UT_OUTPUT, LSB_US_LIMIT);
  mu_assert(u == 1024, "Output limit received: %u", u);

  u = lsb_usage(sb, LSB_UT_OUTPUT, LSB_US_LIMIT);
  mu_assert(u == 1024, "Output limit received: %u", u);

  lsb_state s = lsb_get_state(sb);
  mu_assert(s == LSB_RUNNING, "lsb_get_state() received: %d", s);

  e = lsb_destroy(sb, "simple.preserve");
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_destroy_error()
{
  const char* expected = "preserve_global_data could not open: "
    "invaliddir/simple.preserve";
  e = lsb_destroy(NULL, NULL);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  lua_sandbox* sb = lsb_create(NULL, "lua/simple.lua", 65765, 1000,
                               1024);
  mu_assert(sb, "lsb_create() received: NULL");
  int result = lsb_init(sb, NULL);
  mu_assert(result == 0, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));
  e = lsb_destroy(sb, "invaliddir/simple.preserve");
  mu_assert(e, "lsb_destroy() received NULL");
  mu_assert(strcmp(e, expected) == 0,
            "lsb_destroy() received: %s", e);
  free(e);
  e = NULL;

  return NULL;
}
static char* test_stop()
{
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/counter.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  lsb_stop_sandbox(sb);
  lua_getglobal(lsb_get_lua(sb), "process");
  lua_pushnumber(lsb_get_lua(sb), 0);
  mu_assert_rv(2, lua_pcall(lsb_get_lua(sb), 1, 2, 0));
  const char *msg = lua_tostring(lsb_get_lua(sb), -1);
  mu_assert(strcmp(LSB_SHUTTING_DOWN, msg) == 0, "received: %s", msg);
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);
  return NULL;
}
static char* test_read_config()
{
  const char *cfg = "memory_limit = 65765\n"
      "instruction_limit = 1000\n"
      "output_limit = 1024\n"
      "array = {'foo', 99}\n"
      "hash  = {foo = 'bar', hash1 = {subfoo = 'subbar'}}\n"
      MODULE_PATH;

  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/read_config.lua", cfg, NULL);
  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s", ret);

  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* test_cjson()
{

  lua_sandbox* sb = lsb_create(NULL, "lua/cjson.lua", 32767, 1000,
                               32767);
  mu_assert(sb, "lsb_create() received: NULL");

  int result = lsb_init(sb, NULL);
  mu_assert(result == 0, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));

  result = process(sb, 0);
  mu_assert(result == 0, "process() received: %d %s", result,
            lsb_get_error(sb));

  e = lsb_destroy(sb, NULL);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  return NULL;
}
static char* benchmark_counter()
{
  int iter = 10000000;

  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/counter.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  clock_t t = clock();
  for (int x = 0; x < iter; ++x) {
    lsb_test_process(sb, 0);
  }
  t = clock() - t;
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);
  printf("benchmark_counter() %g seconds\n", ((double)t) / CLOCKS_PER_SEC
         / iter);

  return NULL;
}
static char* test_serialize_noglobal()
{
  const char* output_file = "serialize_noglobal.preserve";
  const char* expected = "preserve_global_data cannot access the global table";

  lua_sandbox* sb = lsb_create(NULL, "lua/serialize_noglobal.lua",
                               32767, 1000, 1024);
  mu_assert(sb, "lsb_create() received: NULL");

  int result = lsb_init(sb, NULL);
  mu_assert(result == 0, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));
  e = lsb_destroy(sb, output_file);
  mu_assert(e, "lsb_destroy() received: no error");
  mu_assert(strcmp(e, expected) == 0, "lsb_destroy() received: %s", e);
  free(e);
  mu_assert(file_exists(output_file) == 0, "output file was not cleaned up");

  return NULL;
}
static char* benchmark_counter()
{
  int iter = 10000000;

  lua_sandbox* sb = lsb_create(NULL, "lua/counter.lua", 32000, 10, 0);
  mu_assert(sb, "lsb_create() received: NULL");
  int result = lsb_init(sb, NULL);
  mu_assert(result == 0, "lsb_init() received: %d %s", result,
            lsb_get_error(sb));
  clock_t t = clock();
  for (int x = 0; x < iter; ++x) {
    process(sb, 0);
  }
  t = clock() - t;
  e = lsb_destroy(sb, NULL);
  mu_assert(!e, "lsb_destroy() received: %s", e);
  printf("benchmark_counter() %g seconds\n", ((float)t) / CLOCKS_PER_SEC / iter);

  return NULL;
}
static char* test_serialize_failure()
{
  const char *output_file = "serialize_failure.preserve";
  const char *expected = "serialize_data cannot preserve type 'function'";

  remove(output_file);
  lsb_lua_sandbox *sb = lsb_create(NULL,
                                   "lua/serialize_failure.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");

  lsb_err_value ret = lsb_init(sb, output_file);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  e = lsb_destroy(sb);
  mu_assert(e, "lsb_destroy() received: no error");
  mu_assert(strcmp(e, expected) == 0, "lsb_destroy() received: %s", e);
  free(e);
  e = NULL;
  mu_assert(file_exists(output_file) == 0, "output file was not cleaned up");

  return NULL;
}
static char* benchmark_deserialize()
{
  int iter = 10000;

  clock_t t = clock();
  for (int x = 0; x < iter; ++x) {
    lua_sandbox* sb = lsb_create(NULL, "lua/serialize.lua", 32767, 1000,
                                 1024);
    mu_assert(sb, "lsb_create() received: NULL");

    int result = lsb_init(sb, "output/serialize.data");
    mu_assert(result == 0, "lsb_init() received: %d %s", result,
              lsb_get_error(sb));
    e = lsb_destroy(sb, NULL);
    mu_assert(!e, "lsb_destroy() received: %s", e);
  }
  t = clock() - t;
  printf("benchmark_deserialize() %g seconds\n", ((float)t) / CLOCKS_PER_SEC / iter);

  return NULL;
}
static char* benchmark_lua_types_output()
{
  int iter = 1000000;

  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/output.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  lsb_err_value ret = lsb_init(sb, NULL);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  lsb_add_function(sb, &lsb_test_write_output, "write_output");

  clock_t t = clock();
  for (int x = 0; x < iter; ++x) {
    mu_assert(0 == lsb_test_process(sb, 0), "%s", lsb_get_error(sb));
  }
  t = clock() - t;
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);
  printf("benchmark_lua_types_output() %g seconds\n", ((double)t)
         / CLOCKS_PER_SEC / iter);

  return NULL;
}
static char* test_serialize()
{
  const char *output_file = "serialize.preserve";

  remove(output_file);
  lsb_lua_sandbox *sb = lsb_create(NULL, "lua/serialize.lua", test_cfg, NULL);
  mu_assert(sb, "lsb_create() received: NULL");
  add_ud_module(sb);

  lsb_err_value ret = lsb_init(sb, output_file);
  mu_assert(!ret, "lsb_init() received: %s", ret);
  e = lsb_destroy(sb);
  mu_assert(!e, "lsb_destroy() received: %s", e);

  char *expected = lsb_read_file("output/serialize.lua51.data");
  char *actual = lsb_read_file(output_file);
  mu_assert(strcmp(expected, actual) == 0, "serialization mismatch");
  free(expected);
  free(actual);

  return NULL;
}