Code

frontend: Improved parser error reporting.
[sysdb.git] / t / unit / frontend / parser_test.c
index b48ca2f1830dc607504addc46c5a725cb95a9dfd..8af046a1de93c4ece48f5dbc6c3d3d3bcf983579 100644 (file)
@@ -55,20 +55,24 @@ START_TEST(test_parse)
                /* valid commands */
                { "FETCH host 'host'",   -1,  1, CONNECTION_FETCH  },
                { "FETCH host 'host' FILTER "
-                 "host = 'host'",       -1,  1, CONNECTION_FETCH  },
+                 "age > 60s",           -1,  1, CONNECTION_FETCH  },
+               { "FETCH service "
+                 "'host'.'service'",    -1,  1, CONNECTION_FETCH  },
+               { "FETCH metric "
+                 "'host'.'metric'",     -1,  1, CONNECTION_FETCH  },
 
                { "LIST hosts",          -1,  1, CONNECTION_LIST   },
                { "LIST hosts -- foo",   -1,  1, CONNECTION_LIST   },
                { "LIST hosts;",         -1,  1, CONNECTION_LIST   },
                { "LIST hosts; INVALID", 11,  1, CONNECTION_LIST   },
                { "LIST hosts FILTER "
-                 "host = 'host'",       -1,  1, CONNECTION_LIST   },
+                 "age > 60s",           -1,  1, CONNECTION_LIST   },
                { "LIST services",       -1,  1, CONNECTION_LIST   },
                { "LIST services FILTER "
-                 "service = 'svc'",     -1,  1, CONNECTION_LIST   },
+                 "age > 60s",           -1,  1, CONNECTION_LIST   },
                { "LIST metrics",        -1,  1, CONNECTION_LIST   },
                { "LIST metrics FILTER "
-                 "metric = 'metric'",   -1,  1, CONNECTION_LIST   },
+                 "age > 60s",           -1,  1, CONNECTION_LIST   },
 
                { "LOOKUP hosts",        -1,  1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING "
@@ -77,32 +81,32 @@ START_TEST(test_parse)
                  "host = 'host'",       -1,  1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING "
                  "host =~ 'p' AND "
-                 "service =~ 'p'",      -1,  1, CONNECTION_LOOKUP },
+                 "ANY service =~ 'p'",  -1,  1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING NOT "
                  "host =~ 'p' AND "
-                 "service =~ 'p'",      -1,  1, CONNECTION_LOOKUP },
+                 "ANY service =~ 'p'",  -1,  1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING "
                  "host =~ 'p' AND "
-                 "service =~ 'p' OR "
-                 "service =~ 'r'",      -1,  1, CONNECTION_LOOKUP },
+                 "ANY service =~ 'p' OR "
+                 "ANY service =~ 'r'",  -1,  1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING NOT "
                  "host =~ 'p' AND "
-                 "service =~ 'p' OR "
-                 "service =~ 'r'",      -1,  1, CONNECTION_LOOKUP },
+                 "ANY service =~ 'p' OR "
+                 "ANY service =~ 'r'",  -1,  1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING "
                  "host =~ 'p' "
-                 "FILTER .age > 1D",    -1,  1, CONNECTION_LOOKUP },
+                 "FILTER age > 1D",    -1,   1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING "
                  "host =~ 'p' "
-                 "FILTER .age > 1D AND "
-                 ".interval < 240s" ,   -1,  1, CONNECTION_LOOKUP },
+                 "FILTER age > 1D AND "
+                 "interval < 240s" ,   -1,   1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING "
                  "host =~ 'p' "
-                 "FILTER NOT .age>1D",  -1,  1, CONNECTION_LOOKUP },
+                 "FILTER NOT age>1D",  -1,   1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING "
                  "host =~ 'p' "
-                 "FILTER .age>"
-                 ".interval",           -1,  1, CONNECTION_LOOKUP },
+                 "FILTER age>"
+                 "interval",           -1,   1, CONNECTION_LOOKUP },
 
                { "TIMESERIES 'host'.'metric' "
                  "START 2014-01-01 "
@@ -187,9 +191,7 @@ START_TEST(test_parse)
                  "NOT attribute['foo'] "
                  "IS NULL",             -1,  1, CONNECTION_LOOKUP },
                { "LOOKUP hosts MATCHING "
-                 "host IS NULL",        -1, -1, 0 },
-               { "LOOKUP hosts MATCHING "
-                 "service IS NULL",     -1, -1, 0 },
+                 "ANY service IS NULL", -1, -1, 0 },
 
                /* invalid numeric constants */
                { "LOOKUP hosts MATCHING "
@@ -229,26 +231,31 @@ START_TEST(test_parse)
                { "LIST hosts; INVALID", -1, -1, 0 },
                { "/* some incomplete",  -1, -1, 0 },
 
+               /* invalid commands */
                { "LIST",                -1, -1, 0 },
                { "LIST foo",            -1, -1, 0 },
                { "LIST hosts MATCHING "
                  "host = 'host'",       -1, -1, 0 },
                { "LIST foo FILTER "
-                 "host = 'host'",       -1, -1, 0 },
+                 "age > 60s",           -1, -1, 0 },
                { "FETCH host 'host' MATCHING "
                  "host = 'host'",       -1, -1, 0 },
+               { "FETCH service 'host'",-1, -1, 0 },
+               { "FETCH metric 'host'", -1, -1, 0 },
+               { "FETCH host "
+                 "'host'.'localhost'",  -1, -1, 0 },
                { "FETCH foo 'host'",    -1, -1, 0 },
                { "FETCH foo 'host' FILTER "
-                 "host = 'host'",       -1, -1, 0 },
+                 "age > 60s",           -1, -1, 0 },
 
                { "LOOKUP foo",          -1, -1, 0 },
                { "LOOKUP foo MATCHING "
                  "host = 'host'",       -1, -1, 0 },
                { "LOOKUP foo FILTER "
-                 "host = 'host'",       -1, -1, 0 },
+                 "age > 60s",           -1, -1, 0 },
                { "LOOKUP foo MATCHING "
                  "host = 'host' FILTER "
-                 "host = 'host'",       -1, -1, 0 },
+                 "age > 60s",           -1, -1, 0 },
                { "LOOKUP hosts MATCHING "
                  "attribute['foo'] <= "
                  "f || 'oo'",           -1, -1, 0 },
@@ -257,22 +264,26 @@ START_TEST(test_parse)
                  "'f' || oo",           -1, -1, 0 },
        };
 
-       size_t i;
+       sdb_strbuf_t *errbuf = sdb_strbuf_create(64);
        sdb_llist_t *check;
 
+       size_t i;
+
        for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
                sdb_object_t *obj;
                _Bool ok;
 
-               check = sdb_fe_parse(golden_data[i].query, golden_data[i].len);
+               check = sdb_fe_parse(golden_data[i].query,
+                               golden_data[i].len, errbuf);
                if (golden_data[i].expected < 0)
                        ok = check == 0;
                else
                        ok = sdb_llist_len(check) == (size_t)golden_data[i].expected;
 
-               fail_unless(ok, "sdb_fe_parse(%s) = %p (len: %zu); expected: %d",
-                               golden_data[i].query, check, sdb_llist_len(check),
-                               golden_data[i].expected);
+               fail_unless(ok, "sdb_fe_parse(%s) = %p (len: %zu); expected: %d "
+                               "(parser error: %s)", golden_data[i].query, check,
+                               sdb_llist_len(check), golden_data[i].expected,
+                               sdb_strbuf_string(errbuf));
 
                if (! check)
                        continue;
@@ -291,6 +302,8 @@ START_TEST(test_parse)
                sdb_object_deref(obj);
                sdb_llist_destroy(check);
        }
+
+       sdb_strbuf_destroy(errbuf);
 }
 END_TEST
 
@@ -318,39 +331,39 @@ START_TEST(test_parse_matcher)
                { "host = 'host' <garbage>",      13,  MATCHER_EQ },
                { "host &^ 'localhost'",          -1,  -1 },
                /* match hosts by service */
-               { "service < 'name'",             -1,  MATCHER_SERVICE },
-               { "service <= 'name'",            -1,  MATCHER_SERVICE },
-               { "service = 'name'",             -1,  MATCHER_SERVICE },
-               { "service != 'name'",            -1,  MATCHER_SERVICE },
-               { "service >= 'name'",            -1,  MATCHER_SERVICE },
-               { "service > 'name'",             -1,  MATCHER_SERVICE },
-               { "service =~ 'pattern'",         -1,  MATCHER_SERVICE },
-               { "service !~ 'pattern'",         -1,  MATCHER_SERVICE },
-               { "service &^ 'name'",            -1,  -1 },
+               { "ANY service < 'name'",         -1,  MATCHER_ANY },
+               { "ANY service <= 'name'",        -1,  MATCHER_ANY },
+               { "ANY service = 'name'",         -1,  MATCHER_ANY },
+               { "ANY service != 'name'",        -1,  MATCHER_ANY },
+               { "ANY service >= 'name'",        -1,  MATCHER_ANY },
+               { "ANY service > 'name'",         -1,  MATCHER_ANY },
+               { "ANY service =~ 'pattern'",     -1,  MATCHER_ANY },
+               { "ANY service !~ 'pattern'",     -1,  MATCHER_ANY },
+               { "ANY service &^ 'name'",        -1,  -1 },
                /* match hosts by metric */
-               { "metric < 'name'",              -1,  MATCHER_METRIC },
-               { "metric <= 'name'",             -1,  MATCHER_METRIC },
-               { "metric = 'name'",              -1,  MATCHER_METRIC },
-               { "metric != 'name'",             -1,  MATCHER_METRIC },
-               { "metric >= 'name'",             -1,  MATCHER_METRIC },
-               { "metric > 'name'",              -1,  MATCHER_METRIC },
-               { "metric =~ 'pattern'",          -1,  MATCHER_METRIC },
-               { "metric !~ 'pattern'",          -1,  MATCHER_METRIC },
+               { "ANY metric < 'name'",          -1,  MATCHER_ANY },
+               { "ANY metric <= 'name'",         -1,  MATCHER_ANY },
+               { "ANY metric = 'name'",          -1,  MATCHER_ANY },
+               { "ANY metric != 'name'",         -1,  MATCHER_ANY },
+               { "ANY metric >= 'name'",         -1,  MATCHER_ANY },
+               { "ANY metric > 'name'",          -1,  MATCHER_ANY },
+               { "ANY metric =~ 'pattern'",      -1,  MATCHER_ANY },
+               { "ANY metric !~ 'pattern'",      -1,  MATCHER_ANY },
                /* match hosts by attribute */
-               { "attribute < 'name'",           -1,  MATCHER_ATTRIBUTE },
-               { "attribute <= 'name'",          -1,  MATCHER_ATTRIBUTE },
-               { "attribute = 'name'",           -1,  MATCHER_ATTRIBUTE },
-               { "attribute != 'name'",          -1,  MATCHER_ATTRIBUTE },
-               { "attribute >= 'name'",          -1,  MATCHER_ATTRIBUTE },
-               { "attribute > 'name'",           -1,  MATCHER_ATTRIBUTE },
-               { "attribute =~ 'pattern'",       -1,  MATCHER_ATTRIBUTE },
-               { "attribute !~ 'pattern'",       -1,  MATCHER_ATTRIBUTE },
-               { "attribute &^ 'pattern'",       -1,  -1 },
+               { "ANY attribute < 'name'",       -1,  MATCHER_ANY },
+               { "ANY attribute <= 'name'",      -1,  MATCHER_ANY },
+               { "ANY attribute = 'name'",       -1,  MATCHER_ANY },
+               { "ANY attribute != 'name'",      -1,  MATCHER_ANY },
+               { "ANY attribute >= 'name'",      -1,  MATCHER_ANY },
+               { "ANY attribute > 'name'",       -1,  MATCHER_ANY },
+               { "ANY attribute =~ 'pattern'",   -1,  MATCHER_ANY },
+               { "ANY attribute !~ 'pattern'",   -1,  MATCHER_ANY },
+               { "ANY attribute &^ 'pattern'",   -1,  -1 },
                /* composite expressions */
                { "host =~ 'pattern' AND "
-                 "service =~ 'pattern'",         -1,  MATCHER_AND },
+                 "ANY service =~ 'pattern'",     -1,  MATCHER_AND },
                { "host =~ 'pattern' OR "
-                 "service =~ 'pattern'",         -1,  MATCHER_OR },
+                 "ANY service =~ 'pattern'",     -1,  MATCHER_OR },
                { "NOT host = 'host'",            -1,  MATCHER_NOT },
                /* numeric expressions */
                { "attribute['foo'] < 123",       -1,  MATCHER_LT },
@@ -379,72 +392,74 @@ START_TEST(test_parse_matcher)
                { "attribute['foo'] IS NOT NULL", -1,  MATCHER_ISNNULL },
 
                /* object field matchers */
-               { ".name < 'a'",                  -1,  MATCHER_LT },
-               { ".name <= 'a'",                 -1,  MATCHER_LE },
-               { ".name = 'a'",                  -1,  MATCHER_EQ },
-               { ".name != 'a'",                 -1,  MATCHER_NE },
-               { ".name >= 'a'",                 -1,  MATCHER_GE },
-               { ".name > 'a'",                  -1,  MATCHER_GT },
-               { ".last_update < 2014-10-01",    -1,  MATCHER_LT },
-               { ".last_update <= 2014-10-01",   -1,  MATCHER_LE },
-               { ".last_update = 2014-10-01",    -1,  MATCHER_EQ },
-               { ".last_update != 2014-10-01",   -1,  MATCHER_NE },
-               { ".last_update >= 2014-10-01",   -1,  MATCHER_GE },
-               { ".last_update > 2014-10-01",    -1,  MATCHER_GT },
-               { ".Last_Update >= 24D",          -1,  MATCHER_GE },
-               { ".age < 20s",                   -1,  MATCHER_LT },
-               { ".age <= 20s",                  -1,  MATCHER_LE },
-               { ".age = 20s",                   -1,  MATCHER_EQ },
-               { ".age != 20s",                  -1,  MATCHER_NE },
-               { ".age >= 20s",                  -1,  MATCHER_GE },
-               { ".age > 20s",                   -1,  MATCHER_GT },
-               { ".AGE <= 1m",                   -1,  MATCHER_LE },
-               { ".age > 1M",                    -1,  MATCHER_GT },
-               { ".age != 20Y",                  -1,  MATCHER_NE },
-               { ".age <= 2 * .interval",        -1,  MATCHER_LE },
-               { ".interval < 20s",              -1,  MATCHER_LT },
-               { ".interval <= 20s",             -1,  MATCHER_LE },
-               { ".interval = 20s",              -1,  MATCHER_EQ },
-               { ".interval != 20s",             -1,  MATCHER_NE },
-               { ".interval >= 20s",             -1,  MATCHER_GE },
-               { ".interval > 20s",              -1,  MATCHER_GT },
-               { "'be' IN .backend",             -1,  MATCHER_IN },
+               { "name < 'a'",                   -1,  MATCHER_LT },
+               { "name <= 'a'",                  -1,  MATCHER_LE },
+               { "name = 'a'",                   -1,  MATCHER_EQ },
+               { "name != 'a'",                  -1,  MATCHER_NE },
+               { "name >= 'a'",                  -1,  MATCHER_GE },
+               { "name > 'a'",                   -1,  MATCHER_GT },
+               { "last_update < 2014-10-01",     -1,  MATCHER_LT },
+               { "last_update <= 2014-10-01",    -1,  MATCHER_LE },
+               { "last_update = 2014-10-01",     -1,  MATCHER_EQ },
+               { "last_update != 2014-10-01",    -1,  MATCHER_NE },
+               { "last_update >= 2014-10-01",    -1,  MATCHER_GE },
+               { "last_update > 2014-10-01",     -1,  MATCHER_GT },
+               { "Last_Update >= 24D",           -1,  MATCHER_GE },
+               { "age < 20s",                    -1,  MATCHER_LT },
+               { "age <= 20s",                   -1,  MATCHER_LE },
+               { "age = 20s",                    -1,  MATCHER_EQ },
+               { "age != 20s",                   -1,  MATCHER_NE },
+               { "age >= 20s",                   -1,  MATCHER_GE },
+               { "age > 20s",                    -1,  MATCHER_GT },
+               { "AGE <= 1m",                    -1,  MATCHER_LE },
+               { "age > 1M",                     -1,  MATCHER_GT },
+               { "age != 20Y",                   -1,  MATCHER_NE },
+               { "age <= 2 * interval",          -1,  MATCHER_LE },
+               { "interval < 20s",               -1,  MATCHER_LT },
+               { "interval <= 20s",              -1,  MATCHER_LE },
+               { "interval = 20s",               -1,  MATCHER_EQ },
+               { "interval != 20s",              -1,  MATCHER_NE },
+               { "interval >= 20s",              -1,  MATCHER_GE },
+               { "interval > 20s",               -1,  MATCHER_GT },
+               { "'be' IN backend",              -1,  MATCHER_IN },
 
                /* check operator precedence */
                { "host = 'name' OR "
-                 "service = 'name' AND "
-                 "attribute = 'name' OR "
+                 "ANY service = 'name' AND "
+                 "ANY attribute = 'name' OR "
                  "attribute['foo'] = 'bar'",     -1,  MATCHER_OR },
                { "host = 'name' AND "
-                 "service = 'name' AND "
-                 "attribute = 'name' OR "
+                 "ANY service = 'name' AND "
+                 "ANY attribute = 'name' OR "
                  "attribute['foo'] = 'bar'",     -1,  MATCHER_OR },
                { "host = 'name' AND "
-                 "service = 'name' OR "
-                 "attribute = 'name' AND "
+                 "ANY service = 'name' OR "
+                 "ANY attribute = 'name' AND "
                  "attribute['foo'] = 'bar'",     -1,  MATCHER_OR },
                { "(host = 'name' OR "
-                 "service = 'name') AND "
-                 "(attribute = 'name' OR "
+                 "ANY service = 'name') AND "
+                 "(ANY attribute = 'name' OR "
                  "attribute['foo'] = 'bar')",    -1,  MATCHER_AND },
                { "NOT host = 'name' OR "
-                 "service = 'name'",             -1,  MATCHER_OR },
+                 "ANY service = 'name'",         -1,  MATCHER_OR },
                { "NOT host = 'name' OR "
-                 "NOT service = 'name'",         -1,  MATCHER_OR },
+                 "NOT ANY service = 'name'",     -1,  MATCHER_OR },
                { "NOT (host = 'name' OR "
-                 "NOT service = 'name')",        -1,  MATCHER_NOT },
+                 "NOT ANY service = 'name')",    -1,  MATCHER_NOT },
 
                /* syntax errors */
                { "LIST",                         -1, -1 },
                { "foo &^ bar",                   -1, -1 },
-               { ".invalid",                     -1, -1 },
+               { "invalid",                      -1, -1 },
        };
 
+       sdb_strbuf_t *errbuf = sdb_strbuf_create(64);
        size_t i;
 
        for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
                sdb_store_matcher_t *m;
-               m = sdb_fe_parse_matcher(golden_data[i].expr, golden_data[i].len);
+               m = sdb_fe_parse_matcher(golden_data[i].expr,
+                               golden_data[i].len, errbuf);
 
                if (golden_data[i].expected < 0) {
                        fail_unless(m == NULL,
@@ -454,7 +469,8 @@ START_TEST(test_parse_matcher)
                }
 
                fail_unless(m != NULL, "sdb_fe_parse_matcher(%s) = NULL; "
-                               "expected: <matcher>", golden_data[i].expr);
+                               "expected: <matcher> (parser error: %s)",
+                               golden_data[i].expr, sdb_strbuf_string(errbuf));
                fail_unless(M(m)->type == golden_data[i].expected,
                                "sdb_fe_parse_matcher(%s) returned matcher of type %d; "
                                "expected: %d", golden_data[i].expr, M(m)->type,
@@ -462,6 +478,8 @@ START_TEST(test_parse_matcher)
 
                sdb_object_deref(SDB_OBJ(m));
        }
+
+       sdb_strbuf_destroy(errbuf);
 }
 END_TEST
 
@@ -498,42 +516,44 @@ START_TEST(test_parse_expr)
                { "5 % 2",                -1, 0 },
 
                /* queryable fields */
-               { ".last_update",         -1, FIELD_VALUE },
-               { ".AGE",                 -1, FIELD_VALUE },
-               { ".interval",            -1, FIELD_VALUE },
-               { ".Last_Update",         -1, FIELD_VALUE },
-               { ".backend",             -1, FIELD_VALUE },
+               { "last_update",          -1, FIELD_VALUE },
+               { "AGE",                  -1, FIELD_VALUE },
+               { "interval",             -1, FIELD_VALUE },
+               { "Last_Update",          -1, FIELD_VALUE },
+               { "backend",              -1, FIELD_VALUE },
 
                /* attributes */
                { "attribute['foo']",     -1, ATTR_VALUE },
 
                /* arithmetic expressions */
-               { ".age + .age",          -1, SDB_DATA_ADD },
-               { ".age - .age",          -1, SDB_DATA_SUB },
-               { ".age * .age",          -1, SDB_DATA_MUL },
-               { ".age / .age",          -1, SDB_DATA_DIV },
-               { ".age % .age",          -1, SDB_DATA_MOD },
-               { ".age || .age",         -1, SDB_DATA_CONCAT },
+               { "age + age",            -1, SDB_DATA_ADD },
+               { "age - age",            -1, SDB_DATA_SUB },
+               { "age * age",            -1, SDB_DATA_MUL },
+               { "age / age",            -1, SDB_DATA_DIV },
+               { "age % age",            -1, SDB_DATA_MOD },
+               { "age || age",           -1, SDB_DATA_CONCAT },
 
                /* operator precedence */
-               { ".age + .age * .age",   -1, SDB_DATA_ADD },
-               { ".age * .age + .age",   -1, SDB_DATA_ADD },
-               { ".age + .age - .age",   -1, SDB_DATA_SUB },
-               { ".age - .age + .age",   -1, SDB_DATA_ADD },
-               { "(.age + .age) * .age", -1, SDB_DATA_MUL },
-               { ".age + (.age * .age)", -1, SDB_DATA_ADD },
+               { "age + age * age",      -1, SDB_DATA_ADD },
+               { "age * age + age",      -1, SDB_DATA_ADD },
+               { "age + age - age",      -1, SDB_DATA_SUB },
+               { "age - age + age",      -1, SDB_DATA_ADD },
+               { "(age + age) * age",    -1, SDB_DATA_MUL },
+               { "age + (age * age)",    -1, SDB_DATA_ADD },
 
                /* syntax errors */
                { "LIST",                 -1, INT_MAX },
                { "foo &^ bar",           -1, INT_MAX },
-               { ".invalid",             -1, INT_MAX },
+               { "invalid",              -1, INT_MAX },
        };
 
+       sdb_strbuf_t *errbuf = sdb_strbuf_create(64);
        size_t i;
 
        for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
                sdb_store_expr_t *e;
-               e = sdb_fe_parse_expr(golden_data[i].expr, golden_data[i].len);
+               e = sdb_fe_parse_expr(golden_data[i].expr,
+                               golden_data[i].len, errbuf);
 
                if (golden_data[i].expected == INT_MAX) {
                        fail_unless(e == NULL,
@@ -543,7 +563,8 @@ START_TEST(test_parse_expr)
                }
 
                fail_unless(e != NULL, "sdb_fe_parse_expr(%s) = NULL; "
-                               "expected: <expr>", golden_data[i].expr);
+                               "expected: <expr> (parser error: %s)",
+                               golden_data[i].expr, sdb_strbuf_string(errbuf));
                fail_unless(e->type == golden_data[i].expected,
                                "sdb_fe_parse_expr(%s) returned expression of type %d; "
                                "expected: %d", golden_data[i].expr, e->type,
@@ -551,6 +572,8 @@ START_TEST(test_parse_expr)
 
                sdb_object_deref(SDB_OBJ(e));
        }
+
+       sdb_strbuf_destroy(errbuf);
 }
 END_TEST