#include <assert.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "idl/string.h"
#include "idl/processor.h"
#include "CUnit/Theory.h"
CU_Test(idl_union, no_case)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
const char str[] = "union u switch(char) { };";
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ (ret, IDL_RETCODE_SYNTAX_ERROR);
idl_delete_pstate(pstate);
}
CU_Test(idl_union, name_clash)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
const char str[] = "union u switch (long) { case 1: char c; };\n"
"union u switch (long) { case 1: char c; };";
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ (ret, IDL_RETCODE_SEMANTIC_ERROR);
idl_delete_pstate(pstate);
}
CU_Test(idl_union, single_case)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
idl_union_t *u;
idl_case_t *c;
const char str[] = "union u switch(long) { case 1: char c; };";
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
u = (idl_union_t *)pstate->root;
CU_ASSERT_NEQ_FATAL (u, NULL);
CU_ASSERT_FATAL (idl_is_union(u));
CU_ASSERT_EQ_FATAL (idl_type(u->switch_type_spec->type_spec), IDL_LONG);
c = (idl_case_t *)u->cases;
CU_ASSERT_FATAL (idl_is_case(c));
CU_ASSERT_EQ (idl_parent(c), u);
CU_ASSERT_FATAL (idl_is_case_label(c->labels));
CU_ASSERT_EQ (idl_type(c->type_spec), IDL_CHAR);
CU_ASSERT_FATAL (idl_is_declarator(c->declarator));
CU_ASSERT_STREQ (idl_identifier(c->declarator), "c");
c = idl_next(c);
CU_ASSERT_EQ (c, NULL);
idl_delete_pstate(pstate);
}
CU_Test(idl_union, single_default_case)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
idl_union_t *u;
idl_case_t *c;
const char str[] = "union u switch(char) { default: char c; };";
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
u = (idl_union_t *)pstate->root;
CU_ASSERT_NEQ_FATAL (u, NULL);
CU_ASSERT_FATAL (idl_is_union(u));
CU_ASSERT_EQ_FATAL (idl_type(u->switch_type_spec->type_spec), IDL_CHAR);
c = (idl_case_t *)u->cases;
CU_ASSERT_FATAL (idl_is_case(c));
CU_ASSERT_EQ (idl_parent(c), u);
CU_ASSERT_FATAL (idl_is_default_case(c));
CU_ASSERT_EQ (idl_type(c->type_spec), IDL_CHAR);
CU_ASSERT_FATAL (idl_is_declarator(c->declarator));
CU_ASSERT_STREQ (idl_identifier(c->declarator), "c");
c = idl_next(c);
CU_ASSERT_EQ (c, NULL);
idl_delete_pstate(pstate);
}
CU_Test(idl_union, enumerator_switch_type)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
idl_enum_t *e;
idl_enumerator_t *el;
idl_union_t *u;
idl_case_t *c;
const char *str;
str = "enum Color { Red, Yellow, Blue };\n"
"union u switch(Color) { case Red: char c; default: long l; };";
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ (pstate, NULL);
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
e = (idl_enum_t *)pstate->root;
CU_ASSERT_NEQ_FATAL (e, NULL);
CU_ASSERT_FATAL (idl_is_enum(e));
el = e->enumerators;
CU_ASSERT_FATAL (idl_is_enumerator(el));
CU_ASSERT_STREQ (idl_identifier(el), "Red");
el = idl_next(el);
CU_ASSERT_FATAL (idl_is_enumerator(el));
CU_ASSERT_STREQ (idl_identifier(el), "Yellow");
el = idl_next(el);
CU_ASSERT_FATAL (idl_is_enumerator(el));
CU_ASSERT_STREQ (idl_identifier(el), "Blue");
u = (idl_union_t *)idl_next(e);
CU_ASSERT_FATAL (idl_is_union(u));
c = u->cases;
CU_ASSERT_FATAL (idl_is_case(c));
CU_ASSERT_EQ ((uintptr_t)c->labels->const_expr, (uintptr_t)e->enumerators);
idl_delete_pstate(pstate);
}
#define M(name, definitions) "module " name " { " definitions " };"
#define S(name) "struct " name " { char c; };"
#define T(type, name) "typedef " type " " name ";"
#define U(type) "union u switch (" type ") { default: char c; };"
CU_Test(idl_union, typedef_switch_types)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
idl_module_t *m;
idl_typedef_t *t;
idl_union_t *u;
const char *str;
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
str = T("char", "baz") U("baz");
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
t = (idl_typedef_t *)pstate->root;
CU_ASSERT_FATAL (idl_is_typedef(t));
u = idl_next(t);
CU_ASSERT_FATAL (idl_is_union(u));
CU_ASSERT_EQ (t->declarators, u->switch_type_spec->type_spec);
idl_delete_pstate(pstate);
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
str = M("foo", T("char", "baz") U("baz"));
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
m = (idl_module_t *)pstate->root;
CU_ASSERT (idl_is_module(m));
t = (idl_typedef_t *)m->definitions;
CU_ASSERT (idl_is_typedef(t));
u = idl_next(t);
CU_ASSERT (idl_is_union(u));
CU_ASSERT_EQ (t->declarators, u->switch_type_spec->type_spec);
idl_delete_pstate(pstate);
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
str = M("foo", T("char", "baz")) M("bar", U("foo::baz"));
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
m = (idl_module_t *)pstate->root;
CU_ASSERT (idl_is_module(m));
t = (idl_typedef_t *)m->definitions;
CU_ASSERT (idl_is_typedef(t));
m = idl_next(m);
CU_ASSERT (idl_is_module(m));
u = (idl_union_t *)m->definitions;
CU_ASSERT (idl_is_union(u));
CU_ASSERT_EQ (t->declarators, u->switch_type_spec->type_spec);
idl_delete_pstate(pstate);
}
CU_TheoryDataPoints(idl_union, bad_switch_types) = {
CU_DataPoints(const char *,
S("baz") U("baz"),
U("baz"),
M("foo", T("float", "baz")) M("bar", U("foo::baz"))),
CU_DataPoints(idl_retcode_t,
IDL_RETCODE_SEMANTIC_ERROR,
IDL_RETCODE_SEMANTIC_ERROR,
IDL_RETCODE_SEMANTIC_ERROR)
};
CU_Theory((const char *str, idl_retcode_t expret), idl_union, bad_switch_types)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ (pstate, NULL);
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ (ret, expret);
idl_delete_pstate(pstate);
}
static idl_retcode_t parse_string(const char *str, idl_pstate_t **pstatep)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
if ((ret = idl_create_pstate(IDL_FLAG_EXTENDED_DATA_TYPES, NULL, &pstate)))
return ret;
if ((ret = idl_parse_string(pstate, str)))
idl_delete_pstate(pstate);
else
*pstatep = pstate;
return ret;
}
CU_Test(idl_union, default_discriminator_bool)
{
idl_retcode_t ret;
idl_pstate_t *pstate;
char buf[256];
const char *fmt = "union u switch(boolean) { %s };";
static const struct {
const char *cases;
idl_retcode_t result;
enum { FIRST_DISCRIMINANT, DEFAULT_CASE, IMPLICIT_DEFAULT_CASE } condition;
bool discriminant;
} tests[] = {
{ "case FALSE: char x;",
IDL_RETCODE_OK, IMPLICIT_DEFAULT_CASE, true },
{ "case FALSE: char x; default: char y;",
IDL_RETCODE_OK, DEFAULT_CASE, true },
{ "case TRUE: char x;",
IDL_RETCODE_OK, IMPLICIT_DEFAULT_CASE, false },
{ "case TRUE: char x; default: char y;",
IDL_RETCODE_OK, DEFAULT_CASE, false },
{ "case FALSE: char x; case TRUE: char y;",
IDL_RETCODE_OK, FIRST_DISCRIMINANT, false },
{ "case TRUE: char x; case FALSE: char y;",
IDL_RETCODE_OK, FIRST_DISCRIMINANT, true },
{ "case FALSE: char x; case TRUE: char y; default: char z;",
IDL_RETCODE_SEMANTIC_ERROR, DEFAULT_CASE, false }
};
for (size_t i=0, n=sizeof(tests)/sizeof(tests[0]); i < n; i++) {
pstate = NULL;
idl_snprintf(buf, sizeof(buf), fmt, tests[i].cases);
ret = parse_string(buf, &pstate);
CU_ASSERT_EQ (ret, tests[i].result);
if (ret == IDL_RETCODE_OK) {
const idl_case_t *c;
const idl_case_label_t *cl;
const idl_literal_t *l;
const idl_union_t *u;
CU_ASSERT_NEQ_FATAL (pstate, NULL);
u = (const idl_union_t *)pstate->root;
CU_ASSERT_FATAL (idl_is_union(u));
c = u->cases;
CU_ASSERT_FATAL (idl_is_case(c));
cl = c->labels;
CU_ASSERT_FATAL (idl_is_case_label(cl));
CU_ASSERT_FATAL (idl_is_literal(cl->const_expr));
l = cl->const_expr;
CU_ASSERT_EQ_FATAL (idl_type(l), IDL_BOOL);
if (tests[i].condition == DEFAULT_CASE) {
static const idl_mask_t mask = IDL_DEFAULT_CASE_LABEL;
CU_ASSERT_EQ (l->value.bln, !tests[i].discriminant);
CU_ASSERT_NEQ (u->unused_labels, 0);
CU_ASSERT_EQ (idl_mask(u->default_case), mask);
CU_ASSERT_EQ (idl_parent(u->default_case), idl_next(c));
} else if (tests[i].condition == IMPLICIT_DEFAULT_CASE) {
static const idl_mask_t mask = IDL_IMPLICIT_DEFAULT_CASE_LABEL;
CU_ASSERT_EQ (l->value.bln, !tests[i].discriminant);
CU_ASSERT_NEQ (u->unused_labels, 0);
CU_ASSERT_EQ (idl_mask(u->default_case), mask);
CU_ASSERT_EQ (idl_parent(u->default_case), (void *) u);
} else {
static const idl_mask_t mask = IDL_CASE_LABEL;
CU_ASSERT_EQ (l->value.bln, tests[i].discriminant);
CU_ASSERT_EQ (u->unused_labels, 0);
CU_ASSERT_EQ (idl_mask(u->default_case), mask);
CU_ASSERT_EQ (idl_parent(u->default_case), (void *) c);
}
l = u->default_case ? u->default_case->const_expr : NULL;
CU_ASSERT_NEQ_FATAL (l, NULL);
CU_ASSERT_EQ_FATAL (idl_type(l), IDL_BOOL);
CU_ASSERT_EQ (l->value.bln, tests[i].discriminant);
}
idl_delete_pstate(pstate);
}
}
CU_Test(idl_union, default_discriminator_signed_int)
{
#define IMPLICIT \
"union u switch(int8) { case %"PRId8": int8 i1; };"
#define EXPLICIT \
"union u switch(int8) { case %"PRId8": int8 i1; default: int8 i2; };"
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
char buf[256];
static struct {
int8_t label;
int8_t discriminant;
bool branch;
const char *format;
} tests[] = {
{ -1, 0, false, IMPLICIT },
{ 0, 1, false, IMPLICIT },
{ 1, 0, false, IMPLICIT },
{ -1, 0, true, EXPLICIT },
{ 0, 1, true, EXPLICIT },
{ 1, 0, true, EXPLICIT }
};
for (size_t i=0, n=sizeof(tests)/sizeof(tests[0]); i < n; i++) {
const idl_case_t *c;
const idl_case_label_t *cl;
const idl_literal_t *l;
const idl_union_t *u;
pstate = NULL;
idl_snprintf(buf, sizeof(buf), tests[i].format, tests[i].label);
ret = parse_string(buf, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
u = (const idl_union_t *)pstate->root;
CU_ASSERT_FATAL (idl_is_union(u));
c = u->cases;
CU_ASSERT_FATAL (idl_is_case(c));
cl = c->labels;
CU_ASSERT_FATAL (idl_is_case_label(cl));
CU_ASSERT_FATAL (idl_is_literal(cl->const_expr));
l = cl->const_expr;
CU_ASSERT_EQ_FATAL (idl_type(l), IDL_INT8);
CU_ASSERT_EQ (l->value.int8, tests[i].label);
if (tests[i].branch) {
CU_ASSERT_EQ (idl_mask(u->default_case), IDL_DEFAULT_CASE_LABEL);
CU_ASSERT_EQ (idl_parent(u->default_case), idl_next(c));
} else {
CU_ASSERT_EQ (idl_mask(u->default_case), IDL_IMPLICIT_DEFAULT_CASE_LABEL);
CU_ASSERT_EQ (idl_parent(u->default_case), (void *) u);
}
CU_ASSERT_FATAL (idl_is_literal(u->default_case->const_expr));
l = u->default_case->const_expr;
CU_ASSERT_EQ_FATAL (idl_type(l), IDL_INT8);
CU_ASSERT_EQ (l->value.int8, tests[i].discriminant);
idl_delete_pstate(pstate);
}
#undef EXPLICIT
#undef IMPLICIT
}
CU_Test(idl_union, default_discriminator_unsigned_int)
{
#define IMPLICIT \
"union u switch(uint8) { case %" PRIu8 ": uint8 i1; };"
#define EXPLICIT \
"union u switch(uint8) { case %" PRIu8 ": uint8 i1; default: uint8 i2; };"
idl_retcode_t ret;
idl_pstate_t *pstate;
char buf[256];
static const struct {
uint8_t label;
uint8_t discriminant;
bool branch;
const char *format;
} tests[] = {
{ 0, 1, false, IMPLICIT },
{ 1, 0, false, IMPLICIT },
{ 0, 1, true, EXPLICIT },
{ 1, 0, true, EXPLICIT }
};
for (size_t i=0, n=sizeof(tests)/sizeof(tests[0]); i < n; i++) {
const idl_case_t *c;
const idl_case_label_t *cl;
const idl_literal_t *l;
const idl_union_t *u;
pstate = NULL;
idl_snprintf(buf, sizeof(buf), tests[i].format, tests[i].label);
ret = parse_string(buf, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
u = (const idl_union_t *)pstate->root;
CU_ASSERT_FATAL (idl_is_union(u));
c = u->cases;
CU_ASSERT_FATAL (idl_is_case(c));
cl = c->labels;
CU_ASSERT_FATAL (idl_is_case_label(cl));
CU_ASSERT_FATAL (idl_is_literal(cl->const_expr));
l = cl->const_expr;
CU_ASSERT_EQ_FATAL (idl_type(l), IDL_UINT8);
CU_ASSERT_EQ (l->value.uint8, tests[i].label);
if (tests[i].branch) {
CU_ASSERT (u->default_case && u->default_case->const_expr);
CU_ASSERT_EQ (idl_mask(u->default_case), IDL_DEFAULT_CASE_LABEL);
CU_ASSERT_EQ (idl_parent(u->default_case), idl_next(c));
} else {
CU_ASSERT_EQ (idl_mask(u->default_case), IDL_IMPLICIT_DEFAULT_CASE_LABEL);
CU_ASSERT (u->default_case && u->default_case->const_expr);
CU_ASSERT_EQ (idl_parent(u->default_case), (void *) u);
}
CU_ASSERT_NEQ_FATAL (u->default_case->const_expr, NULL);
CU_ASSERT_FATAL (idl_is_literal(u->default_case->const_expr));
l = u->default_case->const_expr;
CU_ASSERT_EQ_FATAL (idl_type(l), IDL_UINT8);
CU_ASSERT_EQ (l->value.uint8, tests[i].discriminant);
idl_delete_pstate(pstate);
}
#undef EXPLICIT
#undef IMPLICIT
}
CU_Test(idl_union, default_discriminator_enum)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
const idl_case_t *c;
const idl_case_label_t *cl;
const idl_enum_t *e1;
const idl_enumerator_t *e1x;
const idl_union_t *u1;
const char *idl;
idl = "enum e1 { e11, e12 };\n"
"union u1 switch(e1) { case e12: char c; };";
pstate = NULL;
ret = parse_string(idl, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
e1 = (const idl_enum_t *)pstate->root;
CU_ASSERT_FATAL (idl_is_enum(e1));
e1x = e1->enumerators;
CU_ASSERT_FATAL (idl_is_enumerator(e1x));
u1 = idl_next(e1);
CU_ASSERT_FATAL (idl_is_union(u1));
CU_ASSERT_EQ_FATAL (idl_mask(u1->default_case), IDL_IMPLICIT_DEFAULT_CASE_LABEL);
CU_ASSERT (u1->default_case && u1->default_case->const_expr == e1x);
idl_delete_pstate(pstate);
idl = "enum e1 { e11, e12 };\n"
"union u1 switch(e1) { case e11: char c; };";
pstate = NULL;
ret = parse_string(idl, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
e1 = (const idl_enum_t *)pstate->root;
CU_ASSERT_FATAL (idl_is_enum(e1));
e1x = idl_next(e1->enumerators);
CU_ASSERT_FATAL (idl_is_enumerator(e1x));
u1 = idl_next(e1);
CU_ASSERT_FATAL (idl_is_union(u1));
CU_ASSERT_EQ_FATAL (idl_mask(u1->default_case), IDL_IMPLICIT_DEFAULT_CASE_LABEL);
CU_ASSERT (u1->default_case && u1->default_case->const_expr == e1x);
idl_delete_pstate(pstate);
idl = "enum e1 { e11, e12 };\n"
"union u1 switch(e1) { case e12: char c; default: long l; };";
pstate = NULL;
ret = parse_string(idl, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
e1 = (const idl_enum_t *)pstate->root;
CU_ASSERT_FATAL (idl_is_enum(e1));
e1x = e1->enumerators;
CU_ASSERT_FATAL (idl_is_enumerator(e1x));
u1 = idl_next(e1);
CU_ASSERT_FATAL (idl_is_union(u1));
c = idl_next(u1->cases);
CU_ASSERT_FATAL (idl_is_case(c));
cl = c->labels;
CU_ASSERT_FATAL (idl_is_case_label(cl));
CU_ASSERT_NEQ (cl->const_expr, NULL);
CU_ASSERT_EQ_FATAL (idl_mask(u1->default_case), IDL_DEFAULT_CASE_LABEL);
CU_ASSERT (u1->default_case && u1->default_case->const_expr == e1x);
idl_delete_pstate(pstate);
idl = "enum e1 { e11, e12 };\n"
"union u1 switch(e1) { case e12: char c; case e11: long l; };";
pstate = NULL;
ret = parse_string(idl, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
e1 = (const idl_enum_t *)pstate->root;
CU_ASSERT_FATAL (idl_is_enum(e1));
e1x = idl_next(e1->enumerators);
CU_ASSERT_FATAL (idl_is_enumerator(e1x));
u1 = idl_next(e1);
CU_ASSERT_FATAL (idl_is_union(u1));
c = u1->cases;
CU_ASSERT_FATAL (idl_is_case(c));
cl = c->labels;
CU_ASSERT_FATAL (idl_is_case_label(cl));
CU_ASSERT_EQ (cl->const_expr, (void *) e1x);
CU_ASSERT_EQ (idl_mask(u1->default_case), IDL_CASE_LABEL);
CU_ASSERT_EQ (u1->default_case, (void *) cl);
idl_delete_pstate(pstate);
idl = "enum e1 { e11, e12 };\n"
"union u1 switch(e1) { case e11: char c; case e12: long l; default: double d; };";
pstate = NULL;
ret = parse_string(idl, &pstate);
CU_ASSERT_EQ (ret, IDL_RETCODE_SEMANTIC_ERROR);
CU_ASSERT_EQ (pstate, NULL);
idl_delete_pstate(pstate);
}
CU_Test(idl_union, two_unions_one_enum)
{
idl_retcode_t ret;
idl_pstate_t *pstate = NULL;
const char str[] = "enum aap { noot, mies };\n"
"union wim switch (aap) { case mies: double zus; };\n"
"union jet switch (aap) { case noot: double zus; };";
ret = idl_create_pstate(0u, NULL, &pstate);
CU_ASSERT_EQ_FATAL (ret, IDL_RETCODE_OK);
CU_ASSERT_NEQ_FATAL (pstate, NULL);
ret = idl_parse_string(pstate, str);
CU_ASSERT_EQ (ret, IDL_RETCODE_OK);
idl_delete_pstate(pstate);
}