From: Dmitry Volyntsev Date: Mon, 15 Jun 2020 15:26:44 +0000 (+0000) Subject: Merging interactive tests into unit_test binary. X-Git-Tag: 0.4.2~27 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=3221341ece44e69593b3be6e2e17db66accc016f;p=njs.git Merging interactive tests into unit_test binary. --- diff --git a/auto/make b/auto/make index c48cf60f..db78c778 100644 --- a/auto/make +++ b/auto/make @@ -249,11 +249,9 @@ lib_test: $NJS_BUILD_DIR/njs_auto_config.h \\ $NJS_BUILD_DIR/utf8_unit_test unit_test: $NJS_BUILD_DIR/njs_auto_config.h \\ - $NJS_BUILD_DIR/njs_unit_test \\ - $NJS_BUILD_DIR/njs_interactive_test + $NJS_BUILD_DIR/njs_unit_test $NJS_BUILD_DIR/njs_unit_test - $NJS_BUILD_DIR/njs_interactive_test test: expect_test unit_test diff --git a/auto/sources b/auto/sources index 66381b57..d5be8ef5 100644 --- a/auto/sources +++ b/auto/sources @@ -65,6 +65,5 @@ NJS_LIB_TEST_SRCS=" \ NJS_TEST_SRCS=" \ src/test/njs_unit_test.c \ - src/test/njs_interactive_test.c \ src/test/njs_benchmark.c \ " diff --git a/src/test/njs_interactive_test.c b/src/test/njs_interactive_test.c deleted file mode 100644 index b44708f9..00000000 --- a/src/test/njs_interactive_test.c +++ /dev/null @@ -1,406 +0,0 @@ - -/* - * Copyright (C) Dmitry Volyntsev - * Copyright (C) NGINX, Inc. - */ - -#include - -#include -#include -#include - -#include "njs_externals_test.h" - - -typedef struct { - njs_str_t script; - njs_str_t ret; -} njs_interactive_test_t; - - -#define ENTER "\n\3" - - -static njs_interactive_test_t njs_test[] = -{ - { njs_str("var a = 3" ENTER - "a * 2" ENTER), - njs_str("6") }, - - { njs_str("var a = \"aa\\naa\"" ENTER - "a" ENTER), - njs_str("aa\naa") }, - - { njs_str("var a = 3" ENTER - "var a = 'str'" ENTER - "a" ENTER), - njs_str("str") }, - - { njs_str("var a = 2" ENTER - "a *= 2" ENTER - "a *= 2" ENTER - "a *= 2" ENTER), - njs_str("16") }, - - { njs_str("var a = 2" ENTER - "var b = 3" ENTER - "a * b" ENTER), - njs_str("6") }, - - { njs_str("var a = 2; var b = 3;" ENTER - "a * b" ENTER), - njs_str("6") }, - - { njs_str("function sq(f) { return f() * f() }" ENTER - "sq(function () { return 3 })" ENTER), - njs_str("9") }, - - /* Temporary indexes */ - - { njs_str("var a = [1,2,3], i; for (i in a) {Object.seal({});}" ENTER), - njs_str("undefined") }, - - { njs_str("var i; for (i in [1,2,3]) {Object.seal({});}" ENTER), - njs_str("undefined") }, - - { njs_str("var a = 'A'; switch (a) {" - "case 0: a += '0';" - "case 1: a += '1';" - "}; a" ENTER), - njs_str("A") }, - - { njs_str("var a = 0; try { a = 5 }" - "catch (e) { a = 9 } finally { a++ } a" ENTER), - njs_str("6") }, - - { njs_str("/abc/i.test('ABC')" ENTER), - njs_str("true") }, - - /* Accumulative mode. */ - - { njs_str("var a = 1" ENTER - "a" ENTER), - njs_str("1") }, - - { njs_str("Number.prototype.test = 'test'" ENTER - "Number.prototype.test" ENTER), - njs_str("test") }, - - /* Error handling */ - - { njs_str("var a = ;" ENTER - "2 + 2" ENTER), - njs_str("4") }, - - { njs_str("function f() { return b;" ENTER), - njs_str("SyntaxError: Unexpected end of input in 1") }, - - { njs_str("function f() { return b;" ENTER - "2 + 2" ENTER), - njs_str("4") }, - - { njs_str("function f() { return function() { return 1" ENTER - "2 + 2" ENTER), - njs_str("4") }, - - { njs_str("function f() { return b;}" ENTER - "2 + 2" ENTER), - njs_str("4") }, - - { njs_str("function f(o) { return o.a.a;}; f{{}}" ENTER - "2 + 2" ENTER), - njs_str("4") }, - - { njs_str("function ff(o) {return o.a.a}" ENTER - "function f(o) {try {return ff(o)} " - "finally {return 1}}" ENTER - "f({})" ENTER), - njs_str("1") }, - - { njs_str("arguments" ENTER - "function(){}()" ENTER), - njs_str("SyntaxError: Unexpected token \"(\" in 1") }, - - /* Backtraces */ - - { njs_str("function ff(o) {return o.a.a}" ENTER - "function f(o) {return ff(o)}" ENTER - "f({})" ENTER), - njs_str("TypeError: cannot get property \"a\" of undefined\n" - " at ff (:1)\n" - " at f (:1)\n" - " at main (native)\n") }, - - { njs_str("function ff(o) {return o.a.a}" ENTER - "function f(o) {try {return ff(o)} " - "finally {return o.a.a}}" ENTER - "f({})" ENTER), - njs_str("TypeError: cannot get property \"a\" of undefined\n" - " at f (:1)\n" - " at main (native)\n") }, - - { njs_str("function f(ff, o) {return ff(o)}" ENTER - "f(function (o) {return o.a.a}, {})" ENTER), - njs_str("TypeError: cannot get property \"a\" of undefined\n" - " at anonymous (:1)\n" - " at f (:1)\n" - " at main (native)\n") }, - - { njs_str("'str'.replace(/t/g," - " function(m) {return m.a.a})" ENTER), - njs_str("TypeError: cannot get property \"a\" of undefined\n" - " at anonymous (:1)\n" - " at String.prototype.replace (native)\n" - " at main (native)\n") }, - - { njs_str("function f(o) {return Object.keys(o)}" ENTER - "f()" ENTER), - njs_str("TypeError: cannot convert undefined argument to object\n" - " at Object.keys (native)\n" - " at f (:1)\n" - " at main (native)\n") }, - - { njs_str("''.repeat(-1)" ENTER), - njs_str("RangeError\n" - " at String.prototype.repeat (native)\n" - " at main (native)\n") }, - - { njs_str("Math.log({}.a.a)" ENTER), - njs_str("TypeError: cannot get property \"a\" of undefined\n" - " at Math.log (native)\n" - " at main (native)\n") }, - - { njs_str("var bound = Math.max.bind(null, {toString(){return {}}}); bound(1)" ENTER), - njs_str("TypeError: Cannot convert object to primitive value\n" - " at Math.max (native)\n" - " at main (native)\n") }, - - { njs_str("Object.prototype()" ENTER), - njs_str("TypeError: (intermediate value)[\"prototype\"] is not a function\n" - " at main (native)\n") }, - - { njs_str("eval()" ENTER), - njs_str("InternalError: Not implemented\n" - " at eval (native)\n" - " at main (native)\n") }, - - { njs_str("$r.method({}.a.a)" ENTER), - njs_str("TypeError: cannot get property \"a\" of undefined\n" - " at $r3.method (native)\n" - " at main (native)\n") }, - - { njs_str("new Function(\n\n@)" ENTER), - njs_str("SyntaxError: Unexpected token \"@\" in 3") }, - - { njs_str("require()" ENTER), - njs_str("TypeError: missing path\n" - " at require (native)\n" - " at main (native)\n") }, - - { njs_str("setTimeout()" ENTER), - njs_str("TypeError: too few arguments\n" - " at setTimeout (native)\n" - " at main (native)\n") }, - - { njs_str("require('crypto').createHash('sha')" ENTER), - njs_str("TypeError: not supported algorithm: \"sha\"\n" - " at crypto.createHash (native)\n" - " at main (native)\n") }, - - { njs_str("var h = require('crypto').createHash('sha1')" ENTER - "h.update([])" ENTER), - njs_str("TypeError: data must be a string\n" - " at Hash.prototype.update (native)\n" - " at main (native)\n") }, - - { njs_str("require('crypto').createHmac('sha1', [])" ENTER), - njs_str("TypeError: key must be a string\n" - " at crypto.createHmac (native)\n" - " at main (native)\n") }, - - { njs_str("var h = require('crypto').createHmac('sha1', 'secret')" ENTER - "h.update([])" ENTER), - njs_str("TypeError: data must be a string\n" - " at Hmac.prototype.update (native)\n" - " at main (native)\n") }, - - { njs_str("function f(o) {function f_in(o) {return o.a.a};" - " return f_in(o)}; f({})" ENTER), - njs_str("TypeError: cannot get property \"a\" of undefined\n" - " at f_in (:1)\n" - " at f (:1)\n" - " at main (native)\n") }, - - { njs_str("function f(o) {var ff = function (o) {return o.a.a};" - " return ff(o)}; f({})" ENTER), - njs_str("TypeError: cannot get property \"a\" of undefined\n" - " at anonymous (:1)\n" - " at f (:1)\n" - " at main (native)\n") }, - - { njs_str("var fs = require('fs');" - "[" - " 'access'," - " 'accessSync'," - " 'readFile'," - " 'readFileSync'," - " 'writeFile'," - " 'writeFileSync'," - " 'appendFile'," - " 'appendFileSync'," - " 'symlink'," - " 'symlinkSync'," - " 'unlink'," - " 'unlinkSync'," - " 'realpath'," - " 'realpathSync'," - "]" - ".every(v=>{ try {fs[v]();} catch (e) { return e.stack.search(`fs.${v} `) >= 0}})" ENTER), - njs_str("true") }, - - { njs_str("parseInt({ toString: function() { return [1] } })" ENTER), - njs_str("TypeError: Cannot convert object to primitive value\n" - " at parseInt (native)\n" - " at main (native)\n") }, - - { njs_str("function f(n) { if (n == 0) { throw 'a'; } return f(n-1); }; f(2)" ENTER), - njs_str("a") }, - - /* Exception in njs_vm_retval_string() */ - - { njs_str("var o = { toString: function() { return [1] } }" ENTER - "o" ENTER), - njs_str("TypeError: Cannot convert object to primitive value") }, - - /* line numbers */ - - { njs_str("/**/(function(){throw Error();})()" ENTER), - njs_str("Error\n" - " at anonymous (:1)\n" - " at main (native)\n") }, - - { njs_str("/***/(function(){throw Error();})()" ENTER), - njs_str("Error\n" - " at anonymous (:1)\n" - " at main (native)\n") }, - - { njs_str("/*\n**/(function(){throw Error();})()" ENTER), - njs_str("Error\n" - " at anonymous (:2)\n" - " at main (native)\n") }, - -}; - - -static njs_int_t -njs_interactive_test(njs_bool_t verbose) -{ - u_char *start, *last, *end; - njs_vm_t *vm; - njs_int_t ret; - njs_str_t s; - njs_uint_t i; - njs_bool_t success; - njs_vm_opt_t options; - njs_interactive_test_t *test; - - vm = NULL; - ret = NJS_ERROR; - - for (i = 0; i < njs_nitems(njs_test); i++) { - - test = &njs_test[i]; - - if (verbose) { - njs_printf("\"%V\"\n", &test->script); - } - - njs_vm_opt_init(&options); - - options.init = 1; - options.accumulative = 1; - options.backtrace = 1; - - vm = njs_vm_create(&options); - if (vm == NULL) { - njs_printf("njs_vm_create() failed\n"); - goto done; - } - - ret = njs_externals_init(vm, NULL); - if (ret != NJS_OK) { - goto done; - } - - start = test->script.start; - last = start + test->script.length; - end = NULL; - - for ( ;; ) { - start = (end != NULL) ? end + njs_length(ENTER) : start; - if (start >= last) { - break; - } - - end = (u_char *) strstr((char *) start, ENTER); - - ret = njs_vm_compile(vm, &start, end); - if (ret == NJS_OK) { - ret = njs_vm_start(vm); - } - } - - if (njs_vm_retval_string(vm, &s) != NJS_OK) { - njs_printf("njs_vm_retval_string() failed\n"); - goto done; - } - - success = njs_strstr_eq(&test->ret, &s); - if (success) { - njs_vm_destroy(vm); - vm = NULL; - continue; - } - - njs_printf("njs_interactive(\"%V\") failed: \"%V\" vs \"%V\"\n", - &test->script, &test->ret, &s); - - goto done; - } - - ret = NJS_OK; - - njs_printf("njs interactive tests passed\n"); - -done: - - if (vm != NULL) { - njs_vm_destroy(vm); - } - - return ret; -} - - -int njs_cdecl -main(int argc, char **argv) -{ - njs_bool_t verbose; - - verbose = 0; - - if (argc > 1) { - switch (argv[1][0]) { - - case 'v': - verbose = 1; - break; - - default: - break; - } - } - - return njs_interactive_test(verbose); -} diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 9292a0bd..c8fe0980 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -17601,6 +17601,280 @@ static njs_unit_test_t njs_regexp_test[] = }; +static njs_unit_test_t njs_shell_test[] = +{ +#define ENTER "\n\3" + + { njs_str("var a = 3" ENTER + "a * 2" ENTER), + njs_str("6") }, + + { njs_str("var a = \"aa\\naa\"" ENTER + "a" ENTER), + njs_str("aa\naa") }, + + { njs_str("var a = 3" ENTER + "var a = 'str'" ENTER + "a" ENTER), + njs_str("str") }, + + { njs_str("var a = 2" ENTER + "a *= 2" ENTER + "a *= 2" ENTER + "a *= 2" ENTER), + njs_str("16") }, + + { njs_str("var a = 2" ENTER + "var b = 3" ENTER + "a * b" ENTER), + njs_str("6") }, + + { njs_str("var a = 2; var b = 3;" ENTER + "a * b" ENTER), + njs_str("6") }, + + { njs_str("function sq(f) { return f() * f() }" ENTER + "sq(function () { return 3 })" ENTER), + njs_str("9") }, + + /* Temporary indexes */ + + { njs_str("var a = [1,2,3], i; for (i in a) {Object.seal({});}" ENTER), + njs_str("undefined") }, + + { njs_str("var i; for (i in [1,2,3]) {Object.seal({});}" ENTER), + njs_str("undefined") }, + + { njs_str("var a = 'A'; " + "switch (a) {" + "case 0: a += '0';" + "case 1: a += '1';" + "}; a" ENTER), + njs_str("A") }, + + { njs_str("var a = 0; try { a = 5 }" + "catch (e) { a = 9 } finally { a++ } a" ENTER), + njs_str("6") }, + + { njs_str("/abc/i.test('ABC')" ENTER), + njs_str("true") }, + + /* Accumulative mode. */ + + { njs_str("var a = 1" ENTER + "a" ENTER), + njs_str("1") }, + + { njs_str("Number.prototype.test = 'test'" ENTER + "Number.prototype.test" ENTER), + njs_str("test") }, + + /* Error handling */ + + { njs_str("var a = ;" ENTER + "2 + 2" ENTER), + njs_str("4") }, + + { njs_str("function f() { return b;" ENTER), + njs_str("SyntaxError: Unexpected end of input in 1") }, + + { njs_str("function f() { return b;" ENTER + "2 + 2" ENTER), + njs_str("4") }, + + { njs_str("function f() { return function() { return 1" ENTER + "2 + 2" ENTER), + njs_str("4") }, + + { njs_str("function f() { return b;}" ENTER + "2 + 2" ENTER), + njs_str("4") }, + + { njs_str("function f(o) { return o.a.a;}; f{{}}" ENTER + "2 + 2" ENTER), + njs_str("4") }, + + { njs_str("function ff(o) {return o.a.a}" ENTER + "function f(o) {try {return ff(o)} " + " finally {return 1}}" ENTER + "f({})" ENTER), + njs_str("1") }, + + { njs_str("arguments" ENTER + "function(){}()" ENTER), + njs_str("SyntaxError: Unexpected token \"(\" in 1") }, + + /* Backtraces */ + + { njs_str("function ff(o) {return o.a.a}" ENTER + "function f(o) {return ff(o)}" ENTER + "f({})" ENTER), + njs_str("TypeError: cannot get property \"a\" of undefined\n" + " at ff (:1)\n" + " at f (:1)\n" + " at main (native)\n") }, + + { njs_str("function ff(o) {return o.a.a}" ENTER + "function f(o) {try {return ff(o)} " + " finally {return o.a.a}}" ENTER + "f({})" ENTER), + njs_str("TypeError: cannot get property \"a\" of undefined\n" + " at f (:1)\n" + " at main (native)\n") }, + + { njs_str("function f(ff, o) {return ff(o)}" ENTER + "f(function (o) {return o.a.a}, {})" ENTER), + njs_str("TypeError: cannot get property \"a\" of undefined\n" + " at anonymous (:1)\n" + " at f (:1)\n" + " at main (native)\n") }, + + { njs_str("'str'.replace(/t/g," + " function(m) {return m.a.a})" ENTER), + njs_str("TypeError: cannot get property \"a\" of undefined\n" + " at anonymous (:1)\n" + " at String.prototype.replace (native)\n" + " at main (native)\n") }, + + { njs_str("function f(o) {return Object.keys(o)}" ENTER + "f()" ENTER), + njs_str("TypeError: cannot convert undefined argument to object\n" + " at Object.keys (native)\n" + " at f (:1)\n" + " at main (native)\n") }, + + { njs_str("''.repeat(-1)" ENTER), + njs_str("RangeError\n" + " at String.prototype.repeat (native)\n" + " at main (native)\n") }, + + { njs_str("Math.log({}.a.a)" ENTER), + njs_str("TypeError: cannot get property \"a\" of undefined\n" + " at Math.log (native)\n" + " at main (native)\n") }, + + { njs_str("var bound = Math.max.bind(null, {toString(){return {}}}); bound(1)" ENTER), + njs_str("TypeError: Cannot convert object to primitive value\n" + " at Math.max (native)\n" + " at main (native)\n") }, + + { njs_str("Object.prototype()" ENTER), + njs_str("TypeError: (intermediate value)[\"prototype\"] is not a function\n" + " at main (native)\n") }, + + { njs_str("eval()" ENTER), + njs_str("InternalError: Not implemented\n" + " at eval (native)\n" + " at main (native)\n") }, + + { njs_str("$r.method({}.a.a)" ENTER), + njs_str("TypeError: cannot get property \"a\" of undefined\n" + " at $r3.method (native)\n" + " at main (native)\n") }, + + { njs_str("new Function(\n\n@)" ENTER), + njs_str("SyntaxError: Unexpected token \"@\" in 3") }, + + { njs_str("require()" ENTER), + njs_str("TypeError: missing path\n" + " at require (native)\n" + " at main (native)\n") }, + + { njs_str("setTimeout()" ENTER), + njs_str("TypeError: too few arguments\n" + " at setTimeout (native)\n" + " at main (native)\n") }, + + { njs_str("require('crypto').createHash('sha')" ENTER), + njs_str("TypeError: not supported algorithm: \"sha\"\n" + " at crypto.createHash (native)\n" + " at main (native)\n") }, + + { njs_str("var h = require('crypto').createHash('sha1')" ENTER + "h.update([])" ENTER), + njs_str("TypeError: data must be a string\n" + " at Hash.prototype.update (native)\n" + " at main (native)\n") }, + + { njs_str("require('crypto').createHmac('sha1', [])" ENTER), + njs_str("TypeError: key must be a string\n" + " at crypto.createHmac (native)\n" + " at main (native)\n") }, + + { njs_str("var h = require('crypto').createHmac('sha1', 'secret')" ENTER + "h.update([])" ENTER), + njs_str("TypeError: data must be a string\n" + " at Hmac.prototype.update (native)\n" + " at main (native)\n") }, + + { njs_str("function f(o) {function f_in(o) {return o.a.a};" + " return f_in(o)}; f({})" ENTER), + njs_str("TypeError: cannot get property \"a\" of undefined\n" + " at f_in (:1)\n" + " at f (:1)\n" + " at main (native)\n") }, + + { njs_str("function f(o) {var ff = function (o) {return o.a.a};" + " return ff(o)}; f({})" ENTER), + njs_str("TypeError: cannot get property \"a\" of undefined\n" + " at anonymous (:1)\n" + " at f (:1)\n" + " at main (native)\n") }, + + { njs_str("var fs = require('fs');" + "[" + " 'access'," + " 'accessSync'," + " 'readFile'," + " 'readFileSync'," + " 'writeFile'," + " 'writeFileSync'," + " 'appendFile'," + " 'appendFileSync'," + " 'symlink'," + " 'symlinkSync'," + " 'unlink'," + " 'unlinkSync'," + " 'realpath'," + " 'realpathSync'," + "]" + ".every(v=>{ try {fs[v]();} catch (e) { return e.stack.search(`fs.${v} `) >= 0}})" ENTER), + njs_str("true") }, + + { njs_str("parseInt({ toString: function() { return [1] } })" ENTER), + njs_str("TypeError: Cannot convert object to primitive value\n" + " at parseInt (native)\n" + " at main (native)\n") }, + + { njs_str("function f(n) { if (n == 0) { throw 'a'; } return f(n-1); }; f(2)" ENTER), + njs_str("a") }, + + /* Exception in njs_vm_retval_string() */ + + { njs_str("var o = { toString: function() { return [1] } }" ENTER + "o" ENTER), + njs_str("TypeError: Cannot convert object to primitive value") }, + + /* line numbers */ + + { njs_str("/**/(function(){throw Error();})()" ENTER), + njs_str("Error\n" + " at anonymous (:1)\n" + " at main (native)\n") }, + + { njs_str("/***/(function(){throw Error();})()" ENTER), + njs_str("Error\n" + " at anonymous (:1)\n" + " at main (native)\n") }, + + { njs_str("/*\n**/(function(){throw Error();})()" ENTER), + njs_str("Error\n" + " at anonymous (:2)\n" + " at main (native)\n") }, + +}; + + typedef struct { njs_bool_t disassemble; njs_bool_t verbose; @@ -17766,6 +18040,103 @@ done: } +static njs_int_t +njs_interactive_test(njs_unit_test_t tests[], size_t num, const char *name, + njs_opts_t *opts, njs_stat_t *stat) +{ + u_char *start, *last, *end; + njs_vm_t *vm; + njs_int_t ret; + njs_str_t s; + njs_uint_t i; + njs_stat_t prev; + njs_bool_t success; + njs_vm_opt_t options; + + vm = NULL; + + prev = *stat; + + ret = NJS_ERROR; + + for (i = 0; i < num; i++) { + + if (opts->verbose) { + njs_printf("\"%V\"\n", &tests[i].script); + } + + njs_vm_opt_init(&options); + + options.init = 1; + options.accumulative = 1; + options.backtrace = 1; + + vm = njs_vm_create(&options); + if (vm == NULL) { + njs_printf("njs_vm_create() failed\n"); + goto done; + } + + if (opts->externals) { + ret = njs_externals_init(vm, NULL); + if (ret != NJS_OK) { + goto done; + } + } + + start = tests[i].script.start; + last = start + tests[i].script.length; + end = NULL; + + for ( ;; ) { + start = (end != NULL) ? end + njs_length(ENTER) : start; + if (start >= last) { + break; + } + + end = (u_char *) strstr((char *) start, ENTER); + + ret = njs_vm_compile(vm, &start, end); + if (ret == NJS_OK) { + ret = njs_vm_start(vm); + } + } + + if (njs_vm_retval_string(vm, &s) != NJS_OK) { + njs_printf("njs_vm_retval_string() failed\n"); + goto done; + } + + success = njs_strstr_eq(&tests[i].ret, &s); + + if (!success) { + njs_printf("njs(\"%V\")\nexpected: \"%V\"\n got: \"%V\"\n", + &tests[i].script, &tests[i].ret, &s); + + stat->failed++; + + } else { + stat->passed++; + } + + njs_vm_destroy(vm); + vm = NULL; + } + + ret = NJS_OK; + +done: + + if (vm != NULL) { + njs_vm_destroy(vm); + } + + njs_unit_test_report(name, &prev, stat); + + return ret; +} + + static njs_int_t njs_timezone_optional_test(njs_opts_t *opts, njs_stat_t *stat) { @@ -18794,6 +19165,12 @@ main(int argc, char **argv) return ret; } + ret = njs_interactive_test(njs_shell_test, njs_nitems(njs_shell_test), + "interactive tests", &opts, &stat); + if (ret != NJS_OK) { + return ret; + } + opts.repeat = 128; ret = njs_unit_test(njs_shared_test, njs_nitems(njs_shared_test),