blob: 955f34f6e002ca34cc735b949edb85971ff01589 [file] [log] [blame]
# Copyright 2013, Big Switch Networks, Inc.
#
# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
# the following special exception:
#
# LOXI Exception
#
# As a special exception to the terms of the EPL, you may distribute libraries
# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
# that copyright and licensing notices generated by LoxiGen are not altered or removed
# from the LoxiGen Libraries and the notice provided below is (i) included in
# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
# documentation for the LoxiGen Libraries, if distributed in binary form.
#
# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
#
# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
# a copy of the EPL at:
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# EPL for the specific language governing permissions and limitations
# under the EPL.
"""
@brief Test case generation functions
@fixme Update the following
The following components are generated.
test_common.[ch]: A collection of common code for tests. Currently
this includes the ability to set the scalar members of an object with
incrementing values and then similarly verify those values
test_scalar_acc.c: Instantiate each type of object, then set and get
scalar values in the objects.
test_list.c: Instantiate each type of list, add an element of each
type the list supports, setting scalar values of the elements.
test_match.c: Various tests for match objects
test_msg.c: Instantiate top level messages
These will move towards unified tests that do the following:
Create or init an object.
Populate the object with incrementing values.
Possibly transform the object in some way (e.g., run the underlying
wire buffer through a parse routine).
Verify that the members all have the appropriate value
Through out, checking the consistency of memory and memory operations
is done with mcheck (if available).
"""
import sys
import c_gen.of_g_legacy as of_g
import c_gen.match as match
import c_gen.flags as flags
from generic_utils import *
import c_gen.type_maps as type_maps
import c_gen.loxi_utils_legacy as loxi_utils
import c_gen.identifiers as identifiers
import util
import test_data
import loxi_globals
from loxi_ir import *
def var_name_map(m_type):
"""
Map a type to a generic variable name for the type.
@param m_type The data type
Used mostly in test code generation, but also for the dup functions.
"""
_var_name_map= dict(
uint8_t="val8",
uint16_t="val16",
uint32_t="val32",
uint64_t="val64",
of_ipv4_t="ipv4",
of_port_no_t="port_no",
of_fm_cmd_t="fm_cmd",
of_wc_bmap_t="wc_bmap",
of_match_bmap_t = "match_bmap",
of_port_name_t="port_name",
of_table_name_t="table_name",
of_desc_str_t="desc_str",
of_serial_num_t="ser_num",
of_str64_t="str64",
of_mac_addr_t="mac_addr",
of_ipv6_t="ipv6",
# Non-scalars; more TBD
of_octets_t="octets",
of_meter_features_t="features",
of_match_t="match",
of_oxm_t="oxm",
of_bsn_vport_t="bsn_vport",
of_table_desc_t="table_desc",
# BSN extensions
of_bsn_vport_q_in_q_t="vport",
of_bitmap_128_t="bitmap_128",
of_bitmap_512_t="bitmap_512",
of_checksum_128_t="checksum_128",
)
if m_type.find("of_list_") == 0:
return "list"
if m_type in of_g.of_mixed_types:
return of_g.of_mixed_types[m_type]["short_name"]
return _var_name_map[m_type]
integer_types = ["uint8_t", "uint16_t", "uint32_t", "uint64_t",
"of_port_no_t", "of_fm_cmd_t", "of_wc_bmap_t",
"of_match_bmap_t", "of_ipv4_t"]
string_types = [ "of_port_name_t", "of_table_name_t",
"of_desc_str_t", "of_serial_num_t", "of_mac_addr_t",
"of_ipv6_t", "of_bitmap_128_t", "of_checksum_128_t",
"of_str64_t", "of_bitmap_512_t"]
scalar_types = integer_types[:]
scalar_types.extend(string_types)
# When embedding an object inside of another object we have to pick a single
# subclass to use, unlike lists where we use all subclasses.
embedded_subclasses = {
'of_oxm_t': 'of_oxm_eth_type',
'of_bsn_vport_t': 'of_bsn_vport_q_in_q',
}
def ignore_member(cls, version, m_name, m_type):
"""
Filter out names or types that either don't have accessors
or those that should not be messed with
or whose types we're not ready to deal with yet.
"""
uclass = loxi_globals.unified.class_by_name(cls)
if not uclass:
return True
if not isinstance(uclass.member_by_name(m_name), OFDataMember):
return True
return loxi_utils.skip_member_name(m_name) or m_type not in scalar_types
def gen_fill_string(out):
out.write("""
/**
* The increment to use on values inside a string
*/
#define OF_TEST_STR_INCR 3
/**
* Fill in a buffer with incrementing values starting
* at the given offset with the given value
* @param buf The buffer to fill
* @param value The value to use for data
* @param len The number of bytes to fill
*/
void
of_test_str_fill(uint8_t *buf, int value, int len)
{
int i;
for (i = 0; i < len; i++) {
*buf = value;
value += OF_TEST_STR_INCR;
buf++;
}
}
/**
* Given a buffer, verify that it's filled as above
* @param buf The buffer to check
* @param value The value to use for data
* @param len The number of bytes to fill
* @return Boolean True on equality (success)
*/
int
of_test_str_check(uint8_t *buf, int value, int len)
{
int i;
uint8_t val8;
val8 = value;
for (i = 0; i < len; i++) {
if (*buf != val8) {
return 0;
}
val8 += OF_TEST_STR_INCR;
buf++;
}
return 1;
}
/**
* Global that determines how octets should be populated
* -1 means use value % MAX (below) to determine length
* 0, 1, ... means used that fixed length
*
* Note: Was 16K, but that made objects too big. May add flexibility
* to call populate with a max parameter for length
*/
int octets_pop_style = -1;
#define OCTETS_MAX_VALUE (128) /* 16K was too big */
#define OCTETS_MULTIPLIER 6367 /* A prime */
int
of_octets_populate(of_octets_t *octets, int value)
{
if (octets_pop_style < 0) {
octets->bytes = (value * OCTETS_MULTIPLIER) % OCTETS_MAX_VALUE;
} else {
octets->bytes = octets_pop_style;
}
if (octets->bytes != 0) {
if ((octets->data = (uint8_t *)MALLOC(octets->bytes)) == NULL) {
return 0;
}
of_test_str_fill(octets->data, value, octets->bytes);
value += 1;
}
return value;
}
int
of_octets_check(of_octets_t *octets, int value)
{
int len;
if (octets_pop_style < 0) {
len = (value * OCTETS_MULTIPLIER) % OCTETS_MAX_VALUE;
TEST_ASSERT(octets->bytes == len);
} else {
TEST_ASSERT(octets->bytes == octets_pop_style);
}
if (octets->bytes != 0) {
TEST_ASSERT(of_test_str_check(octets->data, value, octets->bytes)
== 1);
value += 1;
}
return value;
}
int
of_match_populate(of_match_t *match, of_version_t version, int value)
{
MEMSET(match, 0, sizeof(*match));
match->version = version;
""")
def populate_match_version(wire_version, keys):
out.write("""
if (version == %d) {\
""" % wire_version)
for key in keys:
entry = match.of_match_members[key]
out.write("""
OF_MATCH_MASK_%(ku)s_EXACT_SET(match);
VAR_%(u_type)s_INIT(match->fields.%(key)s, value);
value += 1;
""" % dict(key=key, u_type=entry["m_type"].upper(), ku=key.upper()))
out.write("""
}
""")
for wire_version, match_keys in match.match_keys.items():
populate_match_version(wire_version, match_keys)
out.write("""
if (value % 2) {
/* Sometimes set ipv4 addr masks to non-exact */
match->masks.ipv4_src = 0xffff0000;
match->masks.ipv4_dst = 0xfffff800;
}
/* Restrict values according to masks */
of_memmask(&match->fields, &match->masks, sizeof(match->fields));
return value;
}
int
of_match_check(of_match_t *match, of_version_t version, int value)
{
of_match_t check;
value = of_match_populate(&check, match->version, value);
TEST_ASSERT(value != 0);
TEST_ASSERT(MEMCMP(match, &check, sizeof(check)) == 0);
return value;
}
""")
def gen_common_test_header(out, name):
loxi_utils.gen_c_copy_license(out)
out.write("""
/*
* Test header file
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*/
#if !defined(_TEST_COMMON_H_)
#define _TEST_COMMON_H_
#define DISABLE_WARN_UNUSED_RESULT
#include <loci/loci.h>
#include <locitest/of_dup.h>
#include <locitest/unittest.h>
extern int global_error;
extern int exit_on_error;
/* @todo Make option for -k to continue tests if errors */
#define RUN_TEST(test) do { \\
int rv; \\
TESTCASE(test, rv); \\
if (rv != TEST_PASS) { \\
global_error=1; \\
if (exit_on_error) return(1); \\
} \\
} while(0)
#define TEST_OK(op) TEST_ASSERT((op) == OF_ERROR_NONE)
#define TEST_INDIGO_OK(op) TEST_ASSERT((op) == INDIGO_ERROR_NONE)
/*
* Declarations of functions to populate scalar values in a a class
*/
extern void of_test_str_fill(uint8_t *buf, int value, int len);
extern int of_test_str_check(uint8_t *buf, int value, int len);
extern int of_octets_populate(of_octets_t *octets, int value);
extern int of_octets_check(of_octets_t *octets, int value);
extern int of_match_populate(of_match_t *match, of_version_t version,
int value);
extern int of_match_check(of_match_t *match, of_version_t version, int value);
extern int test_ident_macros(void);
extern int test_dump_objs(void);
/* In test_match_utils.c */
extern int test_match_utils(void);
extern int run_unified_accessor_tests(void);
extern int run_match_tests(void);
extern int run_utility_tests(void);
extern int run_scalar_acc_tests(void);
extern int run_list_tests(void);
extern int run_message_tests(void);
extern int run_validator_tests(void);
extern int run_list_limits_tests(void);
extern int test_ext_objs(void);
extern int test_datafiles(void);
""")
for version in of_g.of_version_range:
for cls in of_g.standard_class_order:
if not loxi_utils.class_in_version(cls, version):
continue
if type_maps.class_is_virtual(cls) and not loxi_utils.class_is_list(cls):
continue
out.write("""
extern int %(cls)s_%(v_name)s_populate(
%(cls)s_t *obj, int value);
extern int %(cls)s_%(v_name)s_check(
%(cls)s_t *obj, int value);
extern int %(cls)s_%(v_name)s_populate_scalars(
%(cls)s_t *obj, int value);
extern int %(cls)s_%(v_name)s_check_scalars(
%(cls)s_t *obj, int value);
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
out.write("\n#endif /* _TEST_COMMON_H_ */\n")
def gen_common_test(out, name):
"""
Generate common test content including main
"""
loxi_utils.gen_c_copy_license(out)
out.write("""
/*
* Common test code for LOCI
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*/
#define DISABLE_WARN_UNUSED_RESULT
#include "loci_log.h"
#include <loci/loci_obj_dump.h>
#include <locitest/unittest.h>
#include <locitest/test_common.h>
/* mcheck is a glibc extension */
#if defined(__linux__)
#include <mcheck.h>
#define MCHECK_INIT mcheck(NULL)
#else
#define MCHECK_INIT do { } while (0)
#endif
/**
* Exit on error if set to 1
*/
int exit_on_error = 1;
/**
* Global error state: 0 is okay, 1 is error
*/
int global_error = 0;
extern int run_unified_accessor_tests(void);
extern int run_match_tests(void);
extern int run_utility_tests(void);
extern int run_scalar_acc_tests(void);
extern int run_list_tests(void);
extern int run_message_tests(void);
/**
* Macros for initializing and checking scalar types
*
* @param var The variable being initialized or checked
* @param val The integer value to set/check against, see below
*
* Note that equality means something special for strings. Each byte
* is initialized to an incrementing value. So check is done against that.
*
*/
""")
for t in scalar_types:
if t in integer_types:
out.write("""
#define VAR_%s_INIT(var, val) var = (%s)(val)
#define VAR_%s_CHECK(var, val) ((var) == (%s)(val))
""" % (t.upper(), t, t.upper(), t))
else:
out.write("""
#define VAR_%s_INIT(var, val) \\
of_test_str_fill((uint8_t *)&(var), val, sizeof(var))
#define VAR_%s_CHECK(var, val) \\
of_test_str_check((uint8_t *)&(var), val, sizeof(var))
""" % (t.upper(), t.upper()))
gen_fill_string(out)
gen_scalar_set_check_funs(out)
gen_unified_accessor_funs(out)
gen_ident_tests(out)
gen_log_test(out)
def gen_message_scalar_test(out, name):
"""
Generate test cases for message objects, scalar accessors
"""
loxi_utils.gen_c_copy_license(out)
out.write("""
/**
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*
* Message-scalar tests for all versions
*/
#include <locitest/test_common.h>
""")
for version in of_g.of_version_range:
v_name = loxi_utils.version_to_name(version)
out.write("""
/**
* Message-scalar tests for version %s
*/
""" % v_name)
for cls in of_g.standard_class_order:
if type_maps.class_is_virtual(cls):
continue
if version in of_g.unified[cls]:
message_scalar_test(out, version, cls)
out.write("""
int
run_scalar_acc_tests(void)
{
""")
for version in of_g.of_version_range:
v_name = loxi_utils.version_to_name(version)
for cls in of_g.standard_class_order:
if type_maps.class_is_virtual(cls):
continue
if version in of_g.unified[cls]:
test_name = "%s_%s" % (cls, v_name)
out.write(" RUN_TEST(%s_scalar);\n" % test_name)
out.write(" return TEST_PASS;\n}\n");
def message_scalar_test(out, version, cls):
"""
Generate one test case for the given version and class
"""
members, member_types = scalar_member_types_get(cls, version)
length = of_g.base_length[(cls, version)]
v_name = loxi_utils.version_to_name(version)
out.write("""
static int
test_%(cls)s_%(v_name)s_scalar(void)
{
%(cls)s_t *obj;
obj = %(cls)s_new(%(v_name)s);
TEST_ASSERT(obj != NULL);
TEST_ASSERT(obj->version == %(v_name)s);
TEST_ASSERT(obj->length == %(length)d);
TEST_ASSERT(obj->parent == NULL);
TEST_ASSERT(obj->object_id == %(u_cls)s);
""" % dict(cls=cls, u_cls=cls.upper(),
v_name=v_name, length=length, version=version))
# If this class is a concrete member of an inheritance hierarchy,
# run the hierarchy's root wire type parser and assert it returns
# the expected object id.
ofclass = loxi_globals.unified.class_by_name(cls)
if ofclass and not ofclass.virtual:
root = ofclass
while root.superclass:
root = root.superclass
if root.virtual:
out.write("""
{
of_object_id_t object_id;
%(root_cls)s_wire_object_id_get(obj, &object_id);
TEST_ASSERT(object_id == %(u_cls)s);
}
""" % dict(root_cls=root.name, u_cls=cls.upper()))
if not type_maps.class_is_virtual(cls):
out.write("""
if (loci_class_metadata[obj->object_id].wire_length_get != NULL) {
int length;
loci_class_metadata[obj->object_id].wire_length_get((of_object_t *)obj, &length);
TEST_ASSERT(length == %(length)d);
}
/* Set up incrementing values for scalar members */
%(cls)s_%(v_name)s_populate_scalars(obj, 1);
/* Check values just set */
TEST_ASSERT(%(cls)s_%(v_name)s_check_scalars(obj, 1) != 0);
""" % dict(cls=cls, u_cls=cls.upper(),
v_name=v_name, length=length, version=version))
out.write("""
%(cls)s_delete(obj);
/* To do: Check memory */
return TEST_PASS;
}
""" % dict(cls=cls))
# Get the members and list of scalar types for members of a given class
def scalar_member_types_get(cls, version):
member_types = []
if not version in of_g.unified[cls]:
return ([], [])
if "use_version" in of_g.unified[cls][version]:
v = of_g.unified[cls][version]["use_version"]
members = of_g.unified[cls][v]["members"]
else:
members = of_g.unified[cls][version]["members"]
# Accumulate variables that are supported
for member in members:
m_type = member["m_type"]
m_name = member["name"]
if (not loxi_utils.type_is_scalar(m_type) or
ignore_member(cls, version, m_name, m_type)):
continue
if not m_type in member_types:
member_types.append(m_type)
return (members, member_types)
def scalar_funs_instance(out, cls, version, members, member_types):
"""
Generate one instance of scalar set/check functions
"""
out.write("""
/**
* Populate the scalar values in obj of type %(cls)s,
* version %(v_name)s
* @param obj Pointer to an object to populate
* @param value The seed value to use in populating the object
* @returns The value after increments for this object's values
*/
int %(cls)s_%(v_name)s_populate_scalars(
%(cls)s_t *obj, int value) {
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
# Declare string types
for t in member_types:
out.write(" %s %s;\n" % (t, var_name_map(t)))
for member in members:
m_type = member["m_type"]
m_name = member["name"]
if (not loxi_utils.type_is_scalar(m_type) or
ignore_member(cls, version, m_name, m_type)):
continue
v_name = var_name_map(m_type);
out.write("""
VAR_%(u_type)s_INIT(%(v_name)s, value);
%(cls)s_%(m_name)s_set(obj, %(v_name)s);
value += 1;
""" % dict(cls=cls, m_name=m_name, u_type=m_type.upper(), v_name=v_name))
out.write("""
return value;
}
""")
out.write("""
/**
* Check scalar values in obj of type %(cls)s,
* version %(v_name)s
* @param obj Pointer to an object to check
* @param value Starting value for checking
* @returns The value after increments for this object's values
*/
int %(cls)s_%(v_name)s_check_scalars(
%(cls)s_t *obj, int value) {
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
for t in member_types:
out.write(" %s %s;\n" % (t, var_name_map(t)))
for member in members:
m_type = member["m_type"]
m_name = member["name"]
if (not loxi_utils.type_is_scalar(m_type) or
ignore_member(cls, version, m_name, m_type)):
continue
v_name = var_name_map(m_type);
out.write("""
%(cls)s_%(m_name)s_get(obj, &%(v_name)s);
TEST_ASSERT(VAR_%(u_type)s_CHECK(%(v_name)s, value));
value += 1;
""" % dict(cls=cls, m_name=m_name, u_type=m_type.upper(), v_name=v_name))
out.write("""
return value;
}
""")
def gen_scalar_set_check_funs(out):
"""
For each object class with scalar members, generate functions that
set and check their values
"""
for version in of_g.of_version_range:
for cls in of_g.standard_class_order:
if type_maps.class_is_virtual(cls):
continue
(members, member_types) = scalar_member_types_get(cls, version)
scalar_funs_instance(out, cls, version, members, member_types)
# Helper function to set up a subclass instance for a test
def setup_instance(out, cls, subcls, instance, v_name, version):
base_type = loxi_utils.list_to_entry_type(cls)
setup_template = """
%(subcls)s_init(%(inst)s, %(v_name)s, -1, 1);
%(cls)s_append_bind(list, %(inst)s);
value = %(subcls)s_%(v_name)s_populate(
%(inst)s, value);
cur_len += %(inst)s->length;
TEST_ASSERT(list->length == cur_len);
"""
out.write("""
/* Append two instances of type %s */
""" % subcls)
for i in range(2):
out.write(setup_template %
dict(inst=instance, subcls=subcls, v_name=v_name,
base_type=base_type, cls=cls,
version=version))
def check_instance(out, cls, subcls, instance, v_name, version, last):
check_template = """
TEST_ASSERT(%(inst)s->object_id == %(elt_name)s);
value = %(subcls)s_%(v_name)s_check(
%(inst)s, value);
TEST_ASSERT(value != 0);
"""
out.write("\n /* Check two instances of type %s */" % instance)
out.write(check_template %
dict(elt_name=loxi_utils.enum_name(subcls),
inst=instance, subcls=subcls,
v_name=loxi_utils.version_to_name(version)))
out.write("""\
TEST_OK(%(cls)s_next(list, &elt));
""" % dict(cls=cls))
out.write(check_template %
dict(elt_name=loxi_utils.enum_name(subcls),
inst=instance, subcls=subcls,
v_name=loxi_utils.version_to_name(version)))
if last:
out.write("""\
TEST_ASSERT(%(cls)s_next(list, &elt) == OF_ERROR_RANGE);
""" % dict(cls=cls))
else:
out.write("""\
TEST_OK(%(cls)s_next(list, &elt));
""" % dict(cls=cls))
# Maybe: Get a map from list class to parent, mem_name of container
def list_test(out, version, cls):
out.write("""
static int
test_%(cls)s_%(v_name)s(void)
{
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
base_type = loxi_utils.list_to_entry_type(cls)
out.write(""" %(cls)s_t *list;
int value = 1;
""" % dict(cls=cls, base_type=base_type))
out.write("""
list = %(cls)s_new(%(v_name)s);
TEST_ASSERT(list != NULL);
TEST_ASSERT(list->version == %(v_name)s);
TEST_ASSERT(list->length == 0);
TEST_ASSERT(list->parent == NULL);
TEST_ASSERT(list->object_id == %(enum_cls)s);
value = %(cls)s_%(v_name)s_populate(list, value);
TEST_ASSERT(value != 0);
""" % dict(cls=cls, base_type=base_type, v_name=loxi_utils.version_to_name(version),
enum_cls=loxi_utils.enum_name(cls)))
out.write("""
/* Now check values */
value = 1;
value = %(cls)s_%(v_name)s_check(list, value);
TEST_ASSERT(value != 0);
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
out.write("""
%(cls)s_delete(list);
return TEST_PASS;
}
""" % dict(cls=cls))
def gen_list_test(out, name):
"""
Generate base line test cases for lists
@param out The file handle to write to
"""
loxi_utils.gen_c_copy_license(out)
out.write("""
/**
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*
* Message-scalar tests for all versions
*/
#include <locitest/test_common.h>
""")
for version in of_g.of_version_range:
v_name = loxi_utils.version_to_name(version)
out.write("""
/**
* Baseline list tests for version %s
*/
""" % v_name)
for cls in of_g.ordered_list_objects:
if version in of_g.unified[cls]:
list_test(out, version, cls)
out.write("""
int
run_list_tests(void)
{
""")
for version in of_g.of_version_range:
v_name = loxi_utils.version_to_name(version)
for cls in of_g.ordered_list_objects:
if version in of_g.unified[cls]:
test_name = "%s_%s" % (cls, v_name)
out.write(" RUN_TEST(%s);\n" % test_name)
out.write("\n return TEST_PASS;\n}\n");
def gen_match_test(out, name):
"""
Generate baseline tests for match functions
"""
loxi_utils.gen_c_copy_license(out)
out.write("""\
/**
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*
* Message-scalar tests for all versions
* @fixme These are mostly hard coded now.
*/
#include <locitest/test_common.h>
static int
test_match_1(void)
{
""")
for version in of_g.of_version_range:
out.write(" of_match_v%(v)d_t *m_v%(v)d;\n" % dict(v=version))
out.write("""\
of_match_t match;
int value = 1;
int idx;
uint32_t exp_value;
/* Verify default values for ip mask map */
for (idx = 0; idx < 64; idx++) {
exp_value = (idx < 32) ? ~((1 << idx) - 1) : 0;
TEST_ASSERT(of_ip_index_to_mask(idx) == exp_value);
if (idx < 32) {
TEST_ASSERT(of_ip_mask_to_index(exp_value) == idx);
}
}
""")
for version in of_g.of_version_range:
out.write("""
/* Create/populate/convert and delete for version %(v_name)s */
m_v%(version)d = of_match_v%(version)d_new(%(v_name)s);
TEST_ASSERT(m_v%(version)d != NULL);
TEST_ASSERT((value = of_match_populate(&match, %(v_name)s, value)) > 0);
TEST_OK(of_match_to_wire_match_v%(version)d(&match, m_v%(version)d));
of_match_v%(version)d_delete(m_v%(version)d);
""" % dict(v_name=loxi_utils.version_to_name(version), version=version))
out.write("""
return TEST_PASS;
}
""")
out.write("""
static int
test_match_2(void)
{
""")
for version in of_g.of_version_range:
out.write(" of_match_v%(v)d_t *m_v%(v)d;\n" % dict(v=version))
out.write("""\
of_match_t match1;
of_match_t match2;
int value = 1;
""")
for version in of_g.of_version_range:
out.write("""
TEST_ASSERT((value = of_match_populate(&match1, %(v_name)s, value)) > 0);
m_v%(version)d = of_match_v%(version)d_new(%(v_name)s);
TEST_ASSERT(m_v%(version)d != NULL);
TEST_OK(of_match_to_wire_match_v%(version)d(&match1, m_v%(version)d));
TEST_OK(of_match_v%(version)d_to_match(m_v%(version)d, &match2));
TEST_ASSERT(memcmp(&match1, &match2, sizeof(match1)) == 0);
of_match_v%(version)d_delete(m_v%(version)d);
""" % dict(v_name=loxi_utils.version_to_name(version), version=version))
out.write("""
return TEST_PASS;
}
""")
out.write("""
static int
test_match_3(void)
{
of_match_t match1;
of_match_t match2;
int value = 1;
of_octets_t octets;
of_object_storage_t storage;
memset(&storage, 0, sizeof(storage));
storage.obj.wbuf = &storage.wbuf;
""")
for version in of_g.of_version_range:
out.write("""
/* Serialize to version %(v_name)s */
TEST_ASSERT((value = of_match_populate(&match1, %(v_name)s, value)) > 0);
TEST_ASSERT(of_match_serialize(%(v_name)s, &match1, &octets) ==
OF_ERROR_NONE);
storage.obj.wbuf->buf = octets.data;
storage.obj.wbuf->alloc_bytes = octets.bytes;
storage.obj.wbuf->current_bytes = octets.bytes;
TEST_ASSERT(of_match_deserialize(%(v_name)s, &match2, &storage.obj, 0, octets.bytes) ==
OF_ERROR_NONE);
TEST_ASSERT(memcmp(&match1, &match2, sizeof(match1)) == 0);
FREE(octets.data);
""" % dict(v_name=loxi_utils.version_to_name(version), version=version))
out.write("""
return TEST_PASS;
}
""")
out.write("""
int run_match_tests(void)
{
RUN_TEST(match_1);
RUN_TEST(match_2);
RUN_TEST(match_3);
RUN_TEST(match_utils);
return TEST_PASS;
}
""")
def gen_msg_test(out, name):
loxi_utils.gen_c_copy_license(out)
out.write("""
/**
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*
* Message-scalar tests for all versions
*/
#include <locitest/test_common.h>
""")
for version in of_g.of_version_range:
for cls in of_g.ordered_messages:
if not (cls, version) in of_g.base_length:
continue
if type_maps.class_is_virtual(cls):
continue
bytes = of_g.base_length[(cls, version)]
out.write("""
static int
test_%(cls)s_create_%(v_name)s(void)
{
%(cls)s_t *obj;
uint8_t *msg_buf;
int value;
of_object_id_t object_id;
int len;
obj = %(cls)s_new(%(v_name)s);
TEST_ASSERT(obj != NULL);
TEST_ASSERT(obj->version == %(v_name)s);
TEST_ASSERT(obj->length == %(bytes)d);
TEST_ASSERT(obj->parent == NULL);
TEST_ASSERT(obj->object_id == %(enum)s);
of_header_wire_object_id_get(obj, &object_id);
TEST_ASSERT(object_id == %(enum)s);
/* Set up incrementing values for scalar members */
value = %(cls)s_%(v_name)s_populate_scalars(obj, 1);
TEST_ASSERT(value != 0);
len = obj->length;
/* Grab the underlying buffer from the message */
of_object_wire_buffer_steal((of_object_t *)obj, &msg_buf);
TEST_ASSERT(msg_buf != NULL);
%(cls)s_delete(obj);
obj = of_object_new_from_message(OF_BUFFER_TO_MESSAGE(msg_buf), len);
TEST_ASSERT(obj != NULL);
/* @fixme Set up all message objects (recursively?) */
value = %(cls)s_%(v_name)s_check_scalars(obj, 1);
TEST_ASSERT(value != 0);
%(cls)s_delete(obj);
return TEST_PASS;
}
""" % dict(cls=cls, version=version, enum=loxi_utils.enum_name(cls),
v_name=loxi_utils.version_to_name(version), bytes=bytes))
out.write("""
int
run_message_tests(void)
{
""")
for version in of_g.of_version_range:
for cls in of_g.ordered_messages:
if not (cls, version) in of_g.base_length:
continue
if type_maps.class_is_virtual(cls):
continue
test_name = "%s_create_%s" % (cls, loxi_utils.version_to_name(version))
out.write(" RUN_TEST(%s);\n" % test_name)
out.write("\n return TEST_PASS;\n}\n");
def gen_list_setup_check(out, cls, version):
"""
Generate functions that populate and check a list with two
of each type of subclass it supports
"""
out.write("""
/**
* Populate a list of type %(cls)s with two of each type of subclass
* @param list Pointer to the list to be populated
* @param value The seed value to use in populating the list
* @returns The value after increments for this object's values
*/
int
%(cls)s_%(v_name)s_populate(
%(cls)s_t *list, int value)
{
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
base_type = loxi_utils.list_to_entry_type(cls)
out.write("""
of_object_t elt;
int cur_len = 0;
static int recursion;
(void) elt;
(void) cur_len;
if (recursion > 0) {
return value;
}
recursion++;
""" % dict(cls=cls, base_type=base_type))
sub_classes = type_maps.sub_class_map(base_type, version)
sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
v_name = loxi_utils.version_to_name(version)
if not type_maps.class_is_virtual(base_type):
out.write(" /* No subclasses for %s */\n"% base_type)
out.write(" %s_t *elt_p;\n" % base_type)
out.write("\n elt_p = &elt;\n")
else:
out.write(" /* Declare pointers for each subclass */\n")
for instance, subcls in sub_classes:
out.write(" %s_t *%s;\n" % (subcls, instance))
out.write("\n /* Instantiate pointers for each subclass */\n")
for instance, subcls in sub_classes:
out.write(" %s = &elt;\n" % (instance))
if not type_maps.class_is_virtual(base_type): # No inheritance case
setup_instance(out, cls, base_type, "elt_p", v_name, version)
else:
for instance, subcls in sub_classes:
setup_instance(out, cls, subcls, instance, v_name, version)
out.write("""
recursion--;
return value;
}
""")
out.write("""
/**
* Check a list of type %(cls)s generated by
* %(cls)s_%(v_name)s_populate
* @param list Pointer to the list that was populated
* @param value Starting value for checking
* @returns The value after increments for this object's values
*/
int
%(cls)s_%(v_name)s_check(
%(cls)s_t *list, int value)
{
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
base_type = loxi_utils.list_to_entry_type(cls)
out.write("""
of_object_t elt;
int count = 0;
int rv;
static int recursion;
if (recursion > 0) {
return value;
}
recursion++;
""" % dict(cls=cls, base_type=base_type))
sub_classes = type_maps.sub_class_map(base_type, version)
sub_classes = [(instance, subcls) for (instance, subcls) in sub_classes if not type_maps.class_is_virtual(subcls)]
v_name = loxi_utils.version_to_name(version)
if not type_maps.class_is_virtual(base_type):
entry_count = 2
out.write(" /* No subclasses for %s */\n"% base_type)
out.write(" %s_t *elt_p;\n" % base_type)
out.write("\n elt_p = &elt;\n")
else:
entry_count = 2 * len(sub_classes) # Two of each type appended
out.write(" /* Declare pointers for each subclass */\n")
for instance, subcls in sub_classes:
out.write(" %s_t *%s;\n" % (subcls, instance))
out.write("\n /* Instantiate pointers for each subclass */\n")
for instance, subcls in sub_classes:
out.write(" %s = &elt;\n" % (instance))
if not type_maps.class_is_virtual(base_type) or sub_classes:
out.write(" TEST_OK(%(cls)s_first(list, &elt));\n" % dict(cls=cls))
if not type_maps.class_is_virtual(base_type): # No inheritance case
check_instance(out, cls, base_type, "elt_p", v_name,
version, True)
else:
count = 0
for instance, subcls in sub_classes:
count += 1
check_instance(out, cls, subcls, instance, v_name,
version, count==len(sub_classes))
out.write("""
""" % dict(base_type=base_type))
out.write("""
/* Do an iterate to test the iterator */
%(u_cls)s_ITER(list, &elt, rv) {
count += 1;
}
TEST_ASSERT(rv == OF_ERROR_RANGE);
TEST_ASSERT(count == %(entry_count)d);
/* We shoehorn a test of the dup functions here */
{
%(cls)s_t *dup;
TEST_ASSERT((dup = %(cls)s_dup(list)) != NULL);
TEST_ASSERT(dup->length == list->length);
TEST_ASSERT(dup->object_id == list->object_id);
TEST_ASSERT(dup->version == list->version);
TEST_ASSERT(MEMCMP(OF_OBJECT_BUFFER_INDEX(dup, 0),
OF_OBJECT_BUFFER_INDEX(list, 0), list->length) == 0);
of_object_delete((of_object_t *)dup);
/* And now for the generic dup function */
TEST_ASSERT((dup = (%(cls)s_t *)
of_object_dup(list)) != NULL);
TEST_ASSERT(dup->length == list->length);
TEST_ASSERT(dup->object_id == list->object_id);
TEST_ASSERT(dup->version == list->version);
TEST_ASSERT(MEMCMP(OF_OBJECT_BUFFER_INDEX(dup, 0),
OF_OBJECT_BUFFER_INDEX(list, 0), list->length) == 0);
of_object_delete((of_object_t *)dup);
}
recursion--;
return value;
}
""" % dict(cls=cls, u_cls=cls.upper(), entry_count=entry_count))
def gen_class_setup_check(out, cls, version):
out.write("""
/**
* Populate all members of an object of type %(cls)s
* with incrementing values
* @param obj Pointer to an object to populate
* @param value The seed value to use in populating the object
* @returns The value after increments for this object's values
*/
int
%(cls)s_%(v_name)s_populate(
%(cls)s_t *obj, int value)
{
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
members, member_types = loxi_utils.all_member_types_get(cls, version)
for m_type in member_types:
if loxi_utils.type_is_scalar(m_type) or m_type in ["of_match_t", "of_octets_t"]:
out.write(" %s %s;\n" % (m_type, var_name_map(m_type)))
elif m_type in embedded_subclasses:
subcls = embedded_subclasses[m_type]
out.write(" %s_t *%s;\n" % (subcls, var_name_map(m_type)))
else:
out.write(" %s *%s;\n" % (m_type, var_name_map(m_type)))
out.write("""
/* Run thru accessors after new to ensure okay */
""")
for member in members:
m_type = member["m_type"]
m_name = member["name"]
if loxi_utils.skip_member_name(m_name):
continue
if m_type in embedded_subclasses:
continue
if loxi_utils.type_is_scalar(m_type) or m_type in ["of_match_t", "of_octets_t"]:
out.write("""\
%(cls)s_%(m_name)s_get(obj, &%(var_name)s);
""" % dict(var_name=var_name_map(m_type), cls=cls, m_name=m_name))
else:
sub_cls = m_type[:-2] # Trim _t
out.write("""\
{
%(sub_cls)s_t sub_cls;
/* Test bind */
%(cls)s_%(m_name)s_bind(obj, &sub_cls);
}
""" % dict(var_name=var_name_map(m_type), cls=cls,
m_name=m_name, sub_cls=sub_cls,
v_name=loxi_utils.version_to_name(version)))
out.write("""
value = %(cls)s_%(v_name)s_populate_scalars(
obj, value);
TEST_ASSERT(value != 0);
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
for member in members:
m_type = member["m_type"]
m_name = member["name"]
if loxi_utils.type_is_scalar(m_type): # Handled by call to scalar setup
continue
if loxi_utils.skip_member_name(m_name):
continue
if m_type == "of_match_t":
out.write("""\
value = of_match_populate(&%(var_name)s, %(v_name)s, value);
TEST_ASSERT(value != 0);
%(cls)s_%(m_name)s_set(
obj, &%(var_name)s);
""" % dict(cls=cls, var_name=var_name_map(m_type),
m_name=m_name, v_name=loxi_utils.version_to_name(version)))
elif m_type == "of_octets_t":
out.write("""\
value = of_octets_populate(&%(var_name)s, value);
TEST_ASSERT(value != 0);
%(cls)s_%(m_name)s_set(
obj, &%(var_name)s);
if (octets.bytes) {
FREE(octets.data);
}
""" % dict(var_name=var_name_map(m_type), cls=cls, m_name=m_name))
elif m_type in embedded_subclasses:
sub_cls = embedded_subclasses[m_type]
out.write("""\
%(var_name)s = %(sub_cls)s_new(%(v_name)s);
TEST_ASSERT(%(var_name)s != NULL);
value = %(sub_cls)s_%(v_name)s_populate(
%(var_name)s, value);
TEST_ASSERT(value != 0);
%(cls)s_%(m_name)s_set(
obj, %(var_name)s);
%(sub_cls)s_delete(%(var_name)s);
""" % dict(cls=cls, sub_cls=sub_cls, m_name=m_name, m_type=m_type,
var_name=var_name_map(m_type),
v_name=loxi_utils.version_to_name(version)))
else:
sub_cls = m_type[:-2] # Trim _t
out.write("""
%(var_name)s = %(sub_cls)s_new(%(v_name)s);
TEST_ASSERT(%(var_name)s != NULL);
value = %(sub_cls)s_%(v_name)s_populate(
%(var_name)s, value);
TEST_ASSERT(value != 0);
%(cls)s_%(m_name)s_set(
obj, %(var_name)s);
%(sub_cls)s_delete(%(var_name)s);
""" % dict(cls=cls, sub_cls=sub_cls, m_name=m_name, m_type=m_type,
var_name=var_name_map(m_type),
v_name=loxi_utils.version_to_name(version)))
out.write("""
return value;
}
""")
out.write("""
/**
* Check all members of an object of type %(cls)s
* populated by the above function
* @param obj Pointer to an object to check
* @param value Starting value for checking
* @returns The value after increments for this object's values
*/
int
%(cls)s_%(v_name)s_check(
%(cls)s_t *obj, int value)
{
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
members, member_types = loxi_utils.all_member_types_get(cls, version)
for m_type in member_types:
if loxi_utils.type_is_scalar(m_type): # Handled by call to scalar setup
continue
if loxi_utils.type_is_of_object(m_type):
continue
out.write(" %s %s;\n" % (m_type, var_name_map(m_type)))
out.write("""
value = %(cls)s_%(v_name)s_check_scalars(
obj, value);
TEST_ASSERT(value != 0);
""" % dict(cls=cls, v_name=loxi_utils.version_to_name(version)))
for member in members:
m_type = member["m_type"]
m_name = member["name"]
if loxi_utils.type_is_scalar(m_type): # Handled by call to scalar setup
continue
if loxi_utils.skip_member_name(m_name):
continue
if m_type == "of_match_t":
out.write("""\
%(cls)s_%(m_name)s_get(obj, &%(var_name)s);
value = of_match_check(&%(var_name)s, %(v_name)s, value);
""" % dict(cls=cls, var_name=var_name_map(m_type), m_name=m_name,
v_name=loxi_utils.version_to_name(version)))
elif m_type == "of_octets_t":
out.write("""\
%(cls)s_%(m_name)s_get(obj, &%(var_name)s);
value = of_octets_check(&%(var_name)s, value);
""" % dict(cls=cls, var_name=var_name_map(m_type), m_name=m_name,
v_name=loxi_utils.version_to_name(version)))
elif m_type in embedded_subclasses:
sub_cls = embedded_subclasses[m_type]
out.write("""
{ /* Use get/delete to access on check */
%(sub_cls)s_t *%(m_name)s_ptr;
%(m_name)s_ptr = %(cls)s_%(m_name)s_get(obj);
TEST_ASSERT(%(m_name)s_ptr != NULL);
value = %(sub_cls)s_%(v_name)s_check(
%(m_name)s_ptr, value);
TEST_ASSERT(value != 0);
%(sub_cls)s_delete(%(m_name)s_ptr);
}
""" % dict(cls=cls, sub_cls=sub_cls, m_name=m_name, m_type=m_type,
var_name=var_name_map(m_type),
v_name=loxi_utils.version_to_name(version)))
else:
sub_cls = m_type[:-2] # Trim _t
out.write("""
{ /* Use get/delete to access on check */
%(m_type)s *%(m_name)s_ptr;
%(m_name)s_ptr = %(cls)s_%(m_name)s_get(obj);
TEST_ASSERT(%(m_name)s_ptr != NULL);
value = %(sub_cls)s_%(v_name)s_check(
%(m_name)s_ptr, value);
TEST_ASSERT(value != 0);
%(sub_cls)s_delete(%(m_name)s_ptr);
}
""" % dict(cls=cls, sub_cls=sub_cls, m_name=m_name, m_type=m_type,
var_name=var_name_map(m_type),
v_name=loxi_utils.version_to_name(version)))
out.write("""
/* We shoehorn a test of the dup functions here */
{
%(cls)s_t *dup;
TEST_ASSERT((dup = %(cls)s_dup(obj)) != NULL);
TEST_ASSERT(dup->length == obj->length);
TEST_ASSERT(dup->object_id == obj->object_id);
TEST_ASSERT(dup->version == obj->version);
TEST_ASSERT(MEMCMP(OF_OBJECT_BUFFER_INDEX(dup, 0),
OF_OBJECT_BUFFER_INDEX(obj, 0), obj->length) == 0);
of_object_delete((of_object_t *)dup);
/* And now for the generic dup function */
TEST_ASSERT((dup = (%(cls)s_t *)
of_object_dup(obj)) != NULL);
TEST_ASSERT(dup->length == obj->length);
TEST_ASSERT(dup->object_id == obj->object_id);
TEST_ASSERT(dup->version == obj->version);
TEST_ASSERT(MEMCMP(OF_OBJECT_BUFFER_INDEX(dup, 0),
OF_OBJECT_BUFFER_INDEX(obj, 0), obj->length) == 0);
of_object_delete((of_object_t *)dup);
}
return value;
}
""" % dict(cls=cls))
def unified_accessor_test_case(out, cls, version):
"""
Generate one test case for the given version and class
"""
members, member_types = scalar_member_types_get(cls, version)
length = of_g.base_length[(cls, version)]
v_name = loxi_utils.version_to_name(version)
out.write("""
static int
test_%(cls)s_%(v_name)s(void)
{
%(cls)s_t *obj;
obj = %(cls)s_new(%(v_name)s);
TEST_ASSERT(obj != NULL);
TEST_ASSERT(obj->version == %(v_name)s);
TEST_ASSERT(obj->length == %(length)d);
TEST_ASSERT(obj->parent == NULL);
TEST_ASSERT(obj->object_id == %(u_cls)s);
""" % dict(cls=cls, u_cls=cls.upper(),
v_name=v_name, length=length, version=version))
if (not type_maps.class_is_virtual(cls)) or loxi_utils.class_is_list(cls):
out.write("""
if (loci_class_metadata[obj->object_id].wire_length_get != NULL) {
int length;
loci_class_metadata[obj->object_id].wire_length_get((of_object_t *)obj, &length);
TEST_ASSERT(length == %(length)d);
}
if (loci_class_metadata[obj->object_id].wire_type_get != NULL) {
of_object_id_t obj_id;
loci_class_metadata[obj->object_id].wire_type_get((of_object_t *)obj, &obj_id);
TEST_ASSERT(obj_id == %(u_cls)s);
}
/* Set up incrementing values for members */
TEST_ASSERT(%(cls)s_%(v_name)s_populate(
obj, 1) != 0);
/* Check values just set */
TEST_ASSERT(%(cls)s_%(v_name)s_check(
obj, 1) != 0);
""" % dict(cls=cls, u_cls=cls.upper(),
v_name=v_name, length=length, version=version))
out.write("""
%(cls)s_delete(obj);
/* To do: Check memory */
return TEST_PASS;
}
""" % dict(cls=cls))
def gen_unified_accessor_funs(out):
for version in of_g.of_version_range:
for cls in of_g.standard_class_order:
if not loxi_utils.class_in_version(cls, version):
continue
elif loxi_utils.class_is_list(cls):
gen_list_setup_check(out, cls, version)
elif not type_maps.class_is_virtual(cls):
gen_class_setup_check(out, cls, version)
def gen_unified_accessor_tests(out, name):
loxi_utils.gen_c_copy_license(out)
out.write("""
/**
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*
* Unified simple class instantiation tests for all versions
*/
#include <locitest/test_common.h>
""")
for version in of_g.of_version_range:
for cls in of_g.standard_class_order:
if not loxi_utils.class_in_version(cls, version):
continue
if type_maps.class_is_virtual(cls):
continue
unified_accessor_test_case(out, cls, version)
out.write("""
int
run_unified_accessor_tests(void)
{
""")
for version in of_g.of_version_range:
v_name = loxi_utils.version_to_name(version)
for cls in of_g.standard_class_order:
if not loxi_utils.class_in_version(cls, version):
continue
if type_maps.class_is_virtual(cls):
continue
test_name = "%s_%s" % (cls, v_name)
out.write(" RUN_TEST(%s);\n" % test_name)
out.write(" return TEST_PASS;\n}\n");
################################################################
#
# Object duplication functions
#
# These exercise the accessors to create duplicate objects.
# They are used in the LOCI test shim which sits in an OF
# protocol stream.
#
# TODO
# Resolve version stuff
# Complete list dup
def gen_dup_list(out, cls, version):
ver_name = loxi_utils.version_to_name(version)
elt_type = loxi_utils.list_to_entry_type(cls)
out.write("""
/**
* Duplicate a list of type %(cls)s
* using accessor functions
* @param src Pointer to object to be duplicated
* @returns A new object of type %(cls)s.
*
* The caller is responsible for deleting the returned value
*/
%(cls)s_t *
%(cls)s_%(ver_name)s_dup(
%(cls)s_t *src)
{
of_object_t src_elt;
of_object_t *dst_elt;
int rv;
%(cls)s_t *dst;
if ((dst = %(cls)s_new(src->version)) == NULL) {
return NULL;
}
""" % dict(elt_type=elt_type, cls=cls, ver_name=ver_name))
out.write("""
%(u_cls)s_ITER(src, &src_elt, rv) {
if ((dst_elt = %(elt_type)s_%(ver_name)s_dup(&src_elt)) == NULL) {
of_object_delete((of_object_t *)dst);
return NULL;
}
_TRY_FREE(%(cls)s_append(dst, dst_elt),
dst, NULL);
of_object_delete((of_object_t *)dst_elt);
}
return dst;
}
""" % dict(u_cls=cls.upper(), elt_type=elt_type, cls=cls, ver_name=ver_name))
def gen_dup_inheritance(out, cls, version):
ver_name = loxi_utils.version_to_name(version)
out.write("""
/**
* Duplicate a super class object of type %(cls)s
* @param src Pointer to object to be duplicated
* @returns A new object of type %(cls)s.
*
* The caller is responsible for deleting the returned value
*/
of_object_t *
%(cls)s_%(ver_name)s_dup(
of_object_t *src)
{
""" % dict(cls=cls, ver_name=ver_name))
# For each subclass, check if this is an instance of that subclass
sub_classes = type_maps.sub_class_map(cls, version)
for (_, sub_cls) in sub_classes:
sub_enum = sub_cls.upper()
if type_maps.class_is_virtual(sub_cls):
continue
out.write("""
if (src->object_id == %(sub_enum)s) {
return %(sub_cls)s_%(ver_name)s_dup(src);
}
""" % dict(sub_cls=sub_cls, ver_name=ver_name, sub_enum=sub_enum, cls=cls))
out.write("""
return NULL;
}
""")
def gen_dup_cls(out, cls, version):
"""
Generate duplication routine for class cls
"""
ver_name = loxi_utils.version_to_name(version)
out.write("""
/**
* Duplicate an object of type %(cls)s
* using accessor functions
* @param src Pointer to object to be duplicated
* @returns A new object of type %(cls)s.
*
* The caller is responsible for deleting the returned value
*/
%(cls)s_t *
%(cls)s_%(ver_name)s_dup(
%(cls)s_t *src)
{
%(cls)s_t *dst;
""" % dict(cls=cls, ver_name=ver_name))
# Get members and types for the class
members, member_types = loxi_utils.all_member_types_get(cls, version)
# Add declarations for each member type
for m_type in member_types:
if loxi_utils.type_is_scalar(m_type) or m_type in ["of_match_t", "of_octets_t"]:
# Declare instance of these
out.write(" %s %s;\n" % (m_type, var_name_map(m_type)))
elif m_type in embedded_subclasses:
sub_cls = embedded_subclasses[m_type]
out.write("""
%(sub_cls)s_t src_%(v_name)s;
%(sub_cls)s_t *dst_%(v_name)s;
""" % dict(v_name=var_name_map(m_type), sub_cls=sub_cls))
else:
out.write("""
%(m_type)s src_%(v_name)s;
%(m_type)s *dst_%(v_name)s;
""" % dict(m_type=m_type, v_name=var_name_map(m_type)))
out.write("""
if ((dst = %(cls)s_new(src->version)) == NULL) {
return NULL;
}
""" % dict(cls=cls))
for member in members:
m_type = member["m_type"]
m_name = member["name"]
if loxi_utils.skip_member_name(m_name):
continue
if loxi_utils.type_is_scalar(m_type): # Handled by call to scalar setup
out.write("""
%(cls)s_%(m_name)s_get(src, &%(v_name)s);
%(cls)s_%(m_name)s_set(dst, %(v_name)s);
""" % dict(cls=cls, m_name=m_name, v_name=var_name_map(m_type)))
elif m_type in ["of_match_t", "of_octets_t"]:
out.write("""
%(cls)s_%(m_name)s_get(src, &%(v_name)s);
%(cls)s_%(m_name)s_set(dst, &%(v_name)s);
""" % dict(cls=cls, m_name=m_name, v_name=var_name_map(m_type)))
elif m_type in embedded_subclasses:
sub_cls = embedded_subclasses[m_type]
out.write("""
%(cls)s_%(m_name)s_bind(
src, &src_%(v_name)s);
dst_%(v_name)s = %(sub_cls)s_%(ver_name)s_dup(&src_%(v_name)s);
if (dst_%(v_name)s == NULL) {
%(cls)s_delete(dst);
return NULL;
}
%(cls)s_%(m_name)s_set(dst, dst_%(v_name)s);
%(sub_cls)s_delete(dst_%(v_name)s);
""" % dict(sub_cls=sub_cls, cls=cls, m_name=m_name,
v_name=var_name_map(m_type), ver_name=ver_name))
else:
sub_cls = m_type[:-2] # Trim _t
out.write("""
%(cls)s_%(m_name)s_bind(
src, &src_%(v_name)s);
dst_%(v_name)s = %(sub_cls)s_%(ver_name)s_dup(&src_%(v_name)s);
if (dst_%(v_name)s == NULL) {
%(cls)s_delete(dst);
return NULL;
}
%(cls)s_%(m_name)s_set(dst, dst_%(v_name)s);
%(sub_cls)s_delete(dst_%(v_name)s);
""" % dict(sub_cls=sub_cls, cls=cls, m_name=m_name,
v_name=var_name_map(m_type), ver_name=ver_name))
out.write("""
return dst;
}
""")
def gen_version_dup(out=sys.stdout):
"""
Generate duplication routines for each object type
"""
out.write("""
/* Special try macro for duplicating */
#define _TRY_FREE(op, obj, rv) do { \\
int _rv; \\
if ((_rv = (op)) < 0) { \\
LOCI_LOG_ERROR("ERROR %d at %s:%d\\n", _rv, __FILE__, __LINE__); \\
of_object_delete((of_object_t *)(obj)); \\
return (rv); \\
} \\
} while (0)
""")
for version in of_g.of_version_range:
for cls in of_g.standard_class_order:
if not loxi_utils.class_in_version(cls, version):
continue
if type_maps.class_is_inheritance_root(cls):
gen_dup_inheritance(out, cls, version)
elif loxi_utils.class_is_list(cls):
gen_dup_list(out, cls, version)
elif not type_maps.class_is_virtual(cls):
gen_dup_cls(out, cls, version)
def gen_dup(out=sys.stdout):
"""
Generate non-version specific duplication routines for each object type
"""
for cls in of_g.standard_class_order:
out.write("""
of_object_t *
%(cls)s_dup(
of_object_t *src)
{
""" % dict(cls=cls))
for version in of_g.of_version_range:
if not loxi_utils.class_in_version(cls, version):
continue
elif type_maps.class_is_inheritance_root(cls):
pass
elif loxi_utils.class_is_list(cls):
pass
elif type_maps.class_is_virtual(cls):
continue
ver_name = loxi_utils.version_to_name(version)
out.write("""
if (src->version == %(ver_name)s) {
return %(cls)s_%(ver_name)s_dup(src);
}
""" % dict(cls=cls, ver_name=ver_name))
out.write("""
/* Class not supported in given version */
return NULL;
}
""")
def dup_c_gen(out, name):
"""
Generate the C file for duplication functions
"""
loxi_utils.gen_c_copy_license(out)
out.write("""\
/*
* Duplication functions for all OF objects
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*
* These are test functions for exercising accessors. You can call
* of_object_dup for an efficient duplication.
*/
#define DISABLE_WARN_UNUSED_RESULT
#include "loci_log.h"
#include <locitest/of_dup.h>
""")
gen_version_dup(out)
gen_dup(out)
def dup_h_gen(out, name):
"""
Generate the header file for duplication functions
"""
loxi_utils.gen_c_copy_license(out)
out.write("""
/*
* Duplication function header file
*
* AUTOMATICALLY GENERATED FILE. Edits will be lost on regen.
*/
#if !defined(_OF_DUP_H_)
#define _OF_DUP_H_
#include <loci/loci.h>
""")
for cls in of_g.standard_class_order:
out.write("""
extern of_object_t *
%(cls)s_dup(
of_object_t *src);
""" % dict(cls=cls))
for version in of_g.of_version_range:
for cls in of_g.standard_class_order:
if not loxi_utils.class_in_version(cls, version):
continue
elif type_maps.class_is_inheritance_root(cls):
pass
elif loxi_utils.class_is_list(cls):
pass
elif type_maps.class_is_virtual(cls):
continue
ver_name = loxi_utils.version_to_name(version)
out.write("""
extern of_object_t *
%(cls)s_%(ver_name)s_dup(
of_object_t *src);
""" % dict(cls=cls, ver_name=ver_name))
out.write("\n#endif /* _OF_DUP_H_ */\n")
def gen_log_test(out):
"""
Generate test for obj log calls
Define a trivial handler for object logging; call all obj log fns
"""
out.write("""
/**
* Test object dump functions
*/
int
test_dump_objs(void)
{
of_object_t *obj;
FILE *out = fopen("/dev/null", "w");
/* Call each obj dump function */
""")
for version in of_g.of_version_range:
for j, cls in enumerate(of_g.all_class_order):
if not loxi_utils.class_in_version(cls, version):
continue
if type_maps.class_is_virtual(cls):
continue
if cls == "of_bsn_virtual_port_create_request": # test q_in_q
out.write("""
obj = (of_object_t *)%(cls)s_new(%(version)s);
{
of_object_t *vport = of_bsn_vport_q_in_q_new(%(version)s);
%(cls)s_vport_set(obj, vport);
of_object_delete(vport);
}
of_object_dump((loci_writer_f)fprintf, out, obj);
of_object_delete(obj);
""" % dict(cls=cls, version=of_g.of_version_wire2name[version]))
else:
out.write("""
obj = (of_object_t *)%(cls)s_new(%(version)s);
of_object_dump((loci_writer_f)fprintf, out, obj);
of_object_delete(obj);
""" % dict(cls=cls, version=of_g.of_version_wire2name[version]))
out.write("""
fclose(out);
return TEST_PASS;
}
""")
def gen_ident_tests(out):
"""
Generate tests for identifiers
For all idents, instantiate, test version supported macros
For flags, set it, test it, clear it, test it.
"""
out.write("""
/**
* Test cases for all flag accessor macros
* These only test self consistency (and that they compile)
*/
int
test_ident_macros(void)
{
int value __attribute__((unused));
uint32_t flags;
""")
for ident, info in of_g.identifiers.items():
if not identifiers.defined_versions_agree(of_g.identifiers,
of_g.target_version_list,
ident):
# @fixme
continue
out.write(" value = %s;\n" % ident)
for version in of_g.target_version_list:
if version in info["values_by_version"].keys():
out.write(" TEST_ASSERT(%s_SUPPORTED(%s));\n" %
(ident, of_g.of_version_wire2name[version]))
else:
out.write(" TEST_ASSERT(!%s_SUPPORTED(%s));\n" %
(ident, of_g.of_version_wire2name[version]))
if flags.ident_is_flag(ident):
# Grab first supported version
for version in info["values_by_version"]:
break
out.write("""
flags = 0;
%(ident)s_SET(flags, %(ver_name)s);
TEST_ASSERT(flags == %(ident)s_BY_VERSION(%(ver_name)s));
TEST_ASSERT(%(ident)s_TEST(flags, %(ver_name)s));
%(ident)s_CLEAR(flags, %(ver_name)s);
TEST_ASSERT(flags == 0);
TEST_ASSERT(!%(ident)s_TEST(flags, %(ver_name)s));
""" % dict(ident=ident, ver_name=of_g.of_version_wire2name[version]))
out.write("""
return TEST_PASS;
}
""")
def gen_datafiles_tests(out, name):
tests = []
for filename in test_data.list_files():
data = test_data.read(filename)
if not 'c' in data:
continue
name = filename[:-5].replace("/", "_")
tests.append(dict(name=name,
filename=filename,
c=data['c'],
binary=data['binary']))
util.render_template(out, "test_data.c", tests=tests)