<filename>loxigen/c_gen/c_test_gen.py
# 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)

<filename>loxigen/c_gen/c_test_gen.py
# 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)

# 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). 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. # Non-scalars; more TBD # BSN extensions # When embedding an object inside of another object we have to pick a single # subclass to use, unlike lists where we use all subclasses. 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. /**  * 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; if (version == %d) {\ OF_MATCH_MASK_%(ku)s_EXACT_SET(match);         VAR_%(u_type)s_INIT(match->fields.%(key)s, value);         value += 1; } 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; } /*  * 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); 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); #endif /* _TEST_COMMON_H_ */\n") Generate common test content including main /*  * 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.  *  */ #define VAR_%s_INIT(var, val) var = (%s)(val) #define VAR_%s_CHECK(var, val) ((var) == (%s)(val)) #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)) Generate test cases for message objects, scalar accessors /**  *  * AUTOMATICALLY GENERATED FILE.  Edits will be lost on regen.  *  * Message-scalar tests for all versions  */  #include <locitest/test_common.h> /**  * Message-scalar tests for version %s  */ int run_scalar_acc_tests(void) { Generate one test case for the given version and class 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); # 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. {         of_object_id_t object_id;         %(root_cls)s_wire_object_id_get(obj, &object_id);         TEST_ASSERT(object_id == %(u_cls)s);     } 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); %(cls)s_delete(obj);      /* To do: Check memory */     return TEST_PASS; } # Get the members and list of scalar types for members of a given class # Accumulate variables that are supported Generate one instance of scalar set/check functions /**  * 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) { # Declare string types VAR_%(u_type)s_INIT(%(v_name)s, value);     %(cls)s_%(m_name)s_set(obj, %(v_name)s);     value += 1; return value; } /**  * 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) { %(cls)s_%(m_name)s_get(obj, &%(v_name)s);     TEST_ASSERT(VAR_%(u_type)s_CHECK(%(v_name)s, value));     value += 1; return value; } For each object class with scalar members, generate functions that     set and check their values # Helper function to set up a subclass instance for a test %(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); /* Append two instances of type %s */ TEST_ASSERT(%(inst)s->object_id == %(elt_name)s);     value = %(subcls)s_%(v_name)s_check(         %(inst)s, value);     TEST_ASSERT(value != 0); \     TEST_OK(%(cls)s_next(list, &elt)); \     TEST_ASSERT(%(cls)s_next(list, &elt) == OF_ERROR_RANGE); \     TEST_OK(%(cls)s_next(list, &elt)); # Maybe: Get a map from list class to parent, mem_name of container static int test_%(cls)s_%(v_name)s(void) { %(cls)s_t *list;     int value = 1; 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); /* Now check values */     value = 1;     value = %(cls)s_%(v_name)s_check(list, value);     TEST_ASSERT(value != 0); %(cls)s_delete(list);      return TEST_PASS; } Generate base line test cases for lists     @param out The file handle to write to /**  *  * AUTOMATICALLY GENERATED FILE.  Edits will be lost on regen.  *  * Message-scalar tests for all versions  */  #include <locitest/test_common.h> /**  * Baseline list tests for version %s  */ int run_list_tests(void) { Generate baseline tests for match functions \ /**  *  * 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) { \     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);         }     } /* 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); return TEST_PASS; } static int test_match_2(void) { \     of_match_t match1;     of_match_t match2;     int value = 1; 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); return TEST_PASS; } 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; /* 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); return TEST_PASS; } int run_match_tests(void) {     RUN_TEST(match_1);     RUN_TEST(match_2);     RUN_TEST(match_3);     RUN_TEST(match_utils);      return TEST_PASS; } /**  *  * AUTOMATICALLY GENERATED FILE.  Edits will be lost on regen.  *  * Message-scalar tests for all versions  */  #include <locitest/test_common.h> 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; } int run_message_tests(void) { Generate functions that populate and check a list with two     of each type of subclass it supports /**  * 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) { of_object_t elt;     int cur_len = 0;     static int recursion;     (void) elt;     (void) cur_len;      if (recursion > 0) {         return value;     }      recursion++; # No inheritance case recursion--;     return value; } /**  * 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) { of_object_t elt;     int count = 0;     int rv;     static int recursion;      if (recursion > 0) {         return value;     }      recursion++; # Two of each type appended # No inheritance case /* 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; } /**  * 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) { /* Run thru accessors after new to ensure okay */ \     %(cls)s_%(m_name)s_get(obj, &%(var_name)s); # Trim _t \     {         %(sub_cls)s_t sub_cls;          /* Test bind */         %(cls)s_%(m_name)s_bind(obj, &sub_cls);     } value = %(cls)s_%(v_name)s_populate_scalars(         obj, value);     TEST_ASSERT(value != 0); # Handled by call to scalar setup \     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); \     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);     } \     %(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); # Trim _t %(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); return value; } /**  * 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) { # Handled by call to scalar setup value = %(cls)s_%(v_name)s_check_scalars(         obj, value);     TEST_ASSERT(value != 0); # Handled by call to scalar setup \     %(cls)s_%(m_name)s_get(obj, &%(var_name)s);     value = of_match_check(&%(var_name)s, %(v_name)s, value); \     %(cls)s_%(m_name)s_get(obj, &%(var_name)s);     value = of_octets_check(&%(var_name)s, value); { /* 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);     } # Trim _t { /* 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);     } /* 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; } Generate one test case for the given version and class 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); 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); %(cls)s_delete(obj);      /* To do: Check memory */     return TEST_PASS; } /**  *  * AUTOMATICALLY GENERATED FILE.  Edits will be lost on regen.  *  * Unified simple class instantiation tests for all versions  */  #include <locitest/test_common.h> int run_unified_accessor_tests(void) { ################################################################ # # 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 /**  * 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;     } %(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; } /**  * 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) { # For each subclass, check if this is an instance of that subclass if (src->object_id == %(sub_enum)s) {         return %(sub_cls)s_%(ver_name)s_dup(src);     } return NULL; } Generate duplication routine for class cls /**  * 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; # Get members and types for the class # Add declarations for each member type # Declare instance of these %(sub_cls)s_t src_%(v_name)s;     %(sub_cls)s_t *dst_%(v_name)s; %(m_type)s src_%(v_name)s;     %(m_type)s *dst_%(v_name)s; if ((dst = %(cls)s_new(src->version)) == NULL) {         return NULL;     } # Handled by call to scalar setup %(cls)s_%(m_name)s_get(src, &%(v_name)s);     %(cls)s_%(m_name)s_set(dst, %(v_name)s); %(cls)s_%(m_name)s_get(src, &%(v_name)s);     %(cls)s_%(m_name)s_set(dst, &%(v_name)s); %(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); # Trim _t %(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); return dst; } Generate duplication routines for each object type /* 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) Generate non-version specific duplication routines for each object type of_object_t * %(cls)s_dup(     of_object_t *src) { if (src->version == %(ver_name)s) {         return %(cls)s_%(ver_name)s_dup(src);     } /* Class not supported in given version */     return NULL; } Generate the C file for duplication functions \ /*  * 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> Generate the header file for duplication functions /*  * 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> extern of_object_t *     %(cls)s_dup(         of_object_t *src); extern of_object_t *     %(cls)s_%(ver_name)s_dup(         of_object_t *src); #endif /* _OF_DUP_H_ */\n") Generate test for obj log calls      Define a trivial handler for object logging; call all obj log fns /**  * Test object dump functions  */  int test_dump_objs(void) {     of_object_t *obj;      FILE *out = fopen("/dev/null", "w");      /* Call each obj dump function */ # test q_in_q 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); obj = (of_object_t *)%(cls)s_new(%(version)s);     of_object_dump((loci_writer_f)fprintf, out, obj);     of_object_delete(obj); fclose(out);     return TEST_PASS; } Generate tests for identifiers      For all idents, instantiate, test version supported macros     For flags, set it, test it, clear it, test it. /**  * 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; # @fixme # Grab first supported version 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)); return TEST_PASS; }