ossp-pkg/var/var_test.c
/*
** OSSP var -- Variable Expansion
** Copyright (c) 2001-2005 Ralf S. Engelschall <rse@engelschall.com>
** Copyright (c) 2001-2005 The OSSP Project (http://www.ossp.org/)
** Copyright (c) 2001-2005 Cable & Wireless (http://www.cw.com/)
**
** This file is part of OSSP var, a variable expansion
** library which can be found at http://www.ossp.org/pkg/lib/var/.
**
** Permission to use, copy, modify, and distribute this software for
** any purpose with or without fee is hereby granted, provided that
** the above copyright notice and this permission notice appear in all
** copies.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
** var_test.c: library regression test
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "config.h"
#if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC)
#include <dmalloc.h>
#endif
#include "var.h"
#include "ts.h"
/*
**
** ==== VARIABLE LOOKUP CALLBACK ====
**
*/
struct variable {
const char *name;
const unsigned int idx;
const char *data;
};
static const struct variable lookup_vars[] = {
{ "ARRAY", 0, "entry0" },
{ "ARRAY", 1, "entry1" },
{ "ARRAY", 2, "entry2" },
{ "ARRAY", 3, "entry3" },
{ "BAR", 0, "type" },
{ "EMPTY", 0, "" },
{ "FOO", 0, "os" },
{ "HEINZ", 0, "heinz0" },
{ "HEINZ", 1, "heinz1" },
{ "HOME", 0, "/home/regression-tests" },
{ "MULTILINE", 0, "line1\nline2\n" },
{ "NUMBER", 0, "+2" },
{ "NUMEXP", 0, "((16)%5)" },
{ "OSTYPE", 0, "regression-os" },
{ "TERM", 0, "regression-term" },
{ NULL, 0, NULL }
};
static var_rc_t lookup_cb(
var_t *var, void *context,
const char *varname, size_t name_len, int idx,
const char **data, size_t *data_len,
size_t *buffer_size)
{
const struct variable *vars = context;
size_t i, counter, length;
static char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
if (idx >= 0) {
for (i = 0; vars[i].name; ++i) {
if (strncmp(varname, vars[i].name, name_len) == 0 && vars[i].idx == idx) {
*data = vars[i].data;
*data_len = strlen(*data);
*buffer_size = 0;
return VAR_OK;
}
}
}
else {
for (i = 0; vars[i].name; ++i) {
if (strncmp(varname, vars[i].name, name_len) == 0) {
counter = 1;
length = strlen(vars[i].data);
while ( vars[i + counter].data
&& strncmp(varname, vars[i + counter].name, name_len) == 0)
counter++;
if (counter == 1)
sprintf(buf, "%d", (int)length);
else
sprintf(buf, "%d", (int)counter);
*data = buf;
*data_len = strlen(buf);
*buffer_size = 0;
return VAR_OK;
}
}
}
return VAR_ERR_UNDEFINED_VARIABLE;
}
/*
**
** ==== OPERATION CALLBACK ====
**
*/
static var_rc_t
operate_cb(
var_t *var, void *ctx,
const char *op_ptr, size_t op_len,
const char *arg_ptr, size_t arg_len,
const char *val_ptr, size_t val_len,
char **out_ptr, size_t *out_len, size_t *out_size)
{
int i;
if (val_ptr == NULL) {
*out_ptr = "";
*out_len = 0;
*out_size = 0;
return VAR_OK;
}
if (op_len == 6 && strncmp(op_ptr, "return", 6) == 0) {
*out_ptr = malloc(arg_len);
*out_len = arg_len;
*out_size = arg_len;
memcpy(*out_ptr, arg_ptr, arg_len);
return VAR_OK;
}
else if (op_len == 5 && strncmp(op_ptr, "upper", 5) == 0) {
*out_ptr = malloc(val_len);
*out_len = val_len;
*out_size = val_len;
for (i = 0; i < val_len; i++)
(*out_ptr)[i] = (char)toupper((int)(val_ptr[i]));
return VAR_OK;
}
else if (op_len == 5 && strncmp(op_ptr, "lower", 5) == 0) {
*out_ptr = malloc(val_len);
*out_len = val_len;
*out_size = val_len;
for (i = 0; i < val_len; i++)
(*out_ptr)[i] = (char)tolower((int)(val_ptr[i]));
return VAR_OK;
}
else
return VAR_ERR_UNDEFINED_OPERATION;
}
/*
**
** ==== TEST CASES ====
**
*/
TS_TEST(test_expand)
{
struct test_case {
const char *input;
const char *expected;
};
const struct test_case tests[] = {
{ "${HOME}${!!}", "/home/regression-tests${!!}" },
{ "$HOME", "/home/regression-tests" },
{ "${FOO}", "os" },
{ "${BAR}", "type" },
{ "${${FOO:u}${BAR:u}:l:u}", "REGRESSION-OS" },
{ "${UNDEFINED}", "${UNDEFINED}" },
{ "${OSTYPE:#}", "13" },
{ "${EMPTY:-test${FOO}test}", "testostest" },
{ "${EMPTY:-test${FOO:u}test}", "testOStest" },
{ "${TERM:-test${FOO}test}", "regression-term" },
{ "${EMPTY:+FOO}", "" },
{ "${HOME:+test${FOO}test}", "testostest" },
{ "${HOME:+${OS${BAR:u}}}", "regression-os" },
{ "${HOME:+OS${UNDEFINED:u}}", "OS${UNDEFINED:u}" },
{ "${UNDEFINED:+OS${BAR:u}}", "${UNDEFINED:+OS${BAR:u}}" },
{ "${HOME:*heinz}", "" },
{ "${EMPTY:*claus}", "claus" },
{ "${TERM}", "regression-term" },
{ "${HOME:s/reg/bla/}", "/home/blaression-tests" },
{ "${HOME:s/e/bla/}", "/hombla/regression-tests" },
{ "${HOME:s/e/bla/g}", "/hombla/rblagrblassion-tblasts" },
{ "${HOME:s/\\//_/g}", "_home_regression-tests" },
{ "${HOME:s/[eso]/_/g}", "/h_m_/r_gr___i_n-t__t_" },
{ "${HOME:s/[esO]/_/g}", "/hom_/r_gr___ion-t__t_" },
{ "${HOME:s/[esO]/_/gi}", "/h_m_/r_gr___i_n-t__t_" },
{ "${OSTYPE:s/^[re]/_/g}", "_egression-os" },
{ "${EMPTY:s/^[re]/_/g}", "" },
{ "${HOME:s/.*\\x{}/heinz/}", "heinz" },
{ "${HOME:s/e/bla/t}", "/hombla/regression-tests" },
{ "${HOME:s/E/bla/t}", "/home/regression-tests" },
{ "${HOME:s/E/bla/ti}", "/hombla/regression-tests" },
{ "${HOME:s/E/bla/tig}", "/hombla/rblagrblassion-tblasts" },
{ "${HOME:s/^(.*)$/<\\1>/}", "</home/regression-tests>" },
{ "${HOME:o1-5}", "home/" },
{ "${HOME:o1,5}", "home/" },
{ "${HOME:o5,}", "/regression-tests" },
{ "${HOME:o5-}", "/regression-tests" },
{ "${HOME:o7,13}", "egressi" },
{ "${HOME:y/a-z/A-YZ/}", "/HOME/REGRESSION-TESTS" },
{ "${HOME:y/e-g/a-c/}", "/homa/racrassion-tasts" },
{ "${FOO:p/15/../l}", "os............." },
{ "${FOO:p/15/12345/l}", "os1234512345123" },
{ "${FOO:p/15/../r}", ".............os" },
{ "${FOO:p/15/12345/r}", "1234512345123os" },
{ "${FOO:p/15/../c}", "......os......." },
{ "${FOO:p/15/12345/c}", "123451os1234512" },
{ "${FOO:s/os/\\x{4F}\\123/g}", "OS" },
{ "${FOO:s/os/\\1\\x4F\\123/t}", "\\1OS" },
{ "${HOME:s/g(res)s/x\\1x/g}", "/home/rexresxion-tests" },
{ "${HOME:s/(s+)/_\\1_/g}", "/home/regre_ss_ion-te_s_t_s_" },
{ "${HOME:s/\\x65/\\x45/g}", "/homE/rEgrEssion-tEsts" },
{ "${HOME:s/(s*)/x\\1X/g}", "xX/xXhxXoxXmxXexX/xXrxXexXgxXrxXexssXxXixXoxXnxX-xXtxXexsXxXtxsX" },
{ "${HOME:s/./\\\\/g}", "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\" },
{ "${ARRAY[1]}", "entry1", },
{ "${ARRAY[5+4*2-1]}", "${ARRAY[5+4*2-1]}", },
{ "${ARRAY[(5+(3+4)*2)%16]}", "entry3", },
{ "${ARRAY[(5+(3+4)*2)/9]}", "entry2", },
{ "${ARRAY[+1--2]}", "entry3" },
{ "${ARRAY[-1]}", "4" },
{ "${HOME[-1]}", "22" },
{ "${ARRAY[$NUMBER]}", "entry2" },
{ "${ARRAY[$NUMEXP]}", "entry1" },
{ "${ARRAY[$NUMEXP-1]}", "entry0" },
{ "${ARRAY[${UNDEFINED}-1]}", "${ARRAY[${UNDEFINED}-1]}" },
{ "${ARRAY[5/(${UNDEFINED})]}", "${ARRAY[5/(${UNDEFINED})]}" },
{ "[${ARRAY[#]}-]", "entry0-entry1-entry2-entry3-" },
{ "[${ARRAY[#+1]}-]", "entry1-entry2-entry3-" },
{ "-[${ARRAY[#]}:]{1,$NUMBER}-", "-entry1:entry2:-" },
{ "-[${ARRAY[#]}:]{1,3,5}-", "-entry1::-" },
{ "${MULTILINE:s/^/ | /gm}", " | line1\n | line2\n" },
{ "${HOME:%upper}", "/HOME/REGRESSION-TESTS" },
{ "${HOME:%upper:%lower}", "/home/regression-tests" },
{ "${EMPTY:%return($HOME)}", "/home/regression-tests" },
{ "[${ARRAY}:${ARRAY[#]}-]", "entry0:entry0-entry0:entry1-entry0:entry2-entry0:entry3-" },
{ "[${HEINZ[#]}:${ARRAY[#]}-]", "heinz0:entry0-heinz1:entry1-:entry2-:entry3-" },
{ "[${HEINZ[#]}:[${ARRAY[#]}] ]", "heinz0:entry0entry1entry2entry3 heinz1:entry0entry1entry2entry3 " },
{ "[${HEINZ[#]}: [${ARRAY[#]}${ARRAY[#+1]:+, }]${HEINZ[#+1]:+; }]", "heinz0: entry0, entry1, entry2, entry3; heinz1: entry0, entry1, entry2, entry3" },
{ "[${ARRAY[#]}:[${ARRAY[#]},]{1,2,} ]{0,2,}", "entry0:entry1,entry3, entry2:entry1,entry3, " },
};
char *tmp;
size_t tmp_len;
var_rc_t rc;
size_t i;
char buffer[1024];
var_t *var;
char *err;
ts_test_check(TS_CTX, "create environment");
if ((rc = var_create(&var)) != VAR_OK) {
var_strerror(NULL, rc, &err);
ts_test_fail(TS_CTX, "var_create: %s (%d)", err, rc);
return;
}
if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup_cb, lookup_vars)) != VAR_OK) {
var_strerror(NULL, rc, &err);
ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc);
return;
}
if ((rc = var_config(var, VAR_CONFIG_CB_OPERATION, operate_cb, NULL)) != VAR_OK) {
var_strerror(NULL, rc, &err);
ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc);
return;
}
for (i = 0; i < sizeof(tests)/sizeof(struct test_case); i++) {
ts_test_check(TS_CTX, "expansion (#%d): raw input \"%s\"", i, tests[i].input);
rc = var_unescape(var, tests[i].input, strlen(tests[i].input),
buffer, sizeof(buffer), 0);
if (rc != VAR_OK) {
var_strerror(var, rc, &err);
ts_test_fail(TS_CTX, "var_unescape: %s (%d)", err, rc);
continue;
}
ts_test_check(TS_CTX, "expansion (#%d): unescaped input \"%s\"", i, buffer);
rc = var_expand(var, buffer, strlen(buffer), &tmp, &tmp_len, 0);
if (rc != VAR_OK) {
var_strerror(var, rc, &err);
ts_test_fail(TS_CTX, "var_expand: %s (%d)", err, rc);
continue;
}
ts_test_check(TS_CTX, "expansion (#%d): expanded output \"%s\"", i, tmp);
if ( tmp_len != strlen(tests[i].expected)
|| memcmp(tests[i].expected, tmp, tmp_len) != 0) {
ts_test_fail(TS_CTX, "expected \"%s\", got \"%s\"", tests[i].expected, tmp);
free(tmp);
continue;
}
free(tmp);
}
var_destroy(var);
return;
}
TS_TEST(test_format)
{
var_rc_t rc;
var_t *var;
char *err;
char *fmt;
char *expect;
char *got;
ts_test_check(TS_CTX, "create environment");
if ((rc = var_create(&var)) != VAR_OK) {
var_strerror(NULL, rc, &err);
ts_test_fail(TS_CTX, "var_create: %s (%d)", err, rc);
return;
}
if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup_cb, lookup_vars)) != VAR_OK) {
var_strerror(NULL, rc, &err);
ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc);
return;
}
/* check trivial formatting */
fmt = "foo";
expect = "foo";
ts_test_check(TS_CTX, "formatting \"%s\"", fmt);
if ((rc = var_format(var, &got, 1, fmt)) != VAR_OK) {
var_strerror(var, rc, &err);
ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc);
return;
}
if (strcmp(got, expect) != 0) {
ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", expect, got);
free(got);
return;
}
free(got);
/* check real formatting */
fmt = "foo<%s><%d><%c>quux";
expect = "foo<bar><123><x>quux";
ts_test_check(TS_CTX, "formatting \"%s\"", fmt);
if ((rc = var_format(var, &got, 1, fmt, "bar", 123, 'x')) != VAR_OK) {
var_strerror(var, rc, &err);
ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc);
return;
}
if (strcmp(got, expect) != 0) {
ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", expect, got);
free(got);
return;
}
free(got);
/* check combined formatting and expansion */
fmt = "foo<${ARRAY[%d]}>bar";
expect = "foo<entry1>bar";
ts_test_check(TS_CTX, "formatting \"%s\"", fmt);
if ((rc = var_format(var, &got, 1, fmt, 1)) != VAR_OK) {
var_strerror(var, rc, &err);
ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc);
return;
}
if (strcmp(got, expect) != 0) {
ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", expect, got);
free(got);
return;
}
free(got);
var_destroy(var);
}
int main(int argc, char *argv[])
{
ts_suite_t *ts;
int n;
ts = ts_suite_new("OSSP var (Variable Expansion)");
ts_suite_test(ts, test_expand, "expansion");
ts_suite_test(ts, test_format, "formatting");
n = ts_suite_run(ts);
ts_suite_free(ts);
return n;
}