trema/trema-edge

View on GitHub
unittests/lib/utility_test.c

Summary

Maintainability
Test Coverage
/*
 * Unit tests for utility functions.
 * 
 * Author: Yasuhito Takamiya <yasuhito@gmail.com>
 *
 * Copyright (C) 2008-2013 NEC Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include "cmockery_trema.h"
#include "log.h"
#include "ipv4.h"
#include "trema_wrapper.h"
#include "utility.h"
#include "oxm_match.h"
#include "openflow_message.h"
#include "wrapper.h"


/********************************************************************************
 * Setup and teardown
 ********************************************************************************/

static void ( *original_critical )( const char *format, ... );

static void
mock_critical( const char *format, ... ) {
  char output[ 256 ];
  va_list args;
  va_start( args, format );
  vsprintf( output, format, args );
  va_end( args );
  check_expected( output );
}


static void ( *original_abort )( void );

static void
stub_abort() {
  // Do nothing.
}


static void
setup() {
  original_critical = critical;
  critical = mock_critical;

  original_abort = abort;
  trema_abort = stub_abort;
}


static void
teardown() {
  critical = original_critical;
  trema_abort = original_abort;
}


/********************************************************************************
 * Tests.
 ********************************************************************************/

static void
test_die() {
  expect_string( mock_critical, output, "Bye!" );
  die( "Bye!" );
}


static void
test_hash_core() {
  unsigned char bin1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
  unsigned char bin2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };

  assert_true( hash_core( bin1, sizeof( bin1 ) ) == hash_core( bin2, sizeof( bin2 ) ) );
}


static void
test_compare_string() {
  char hello1[] = "Hello World";
  char hello2[] = "Hello World";
  char bye[] = "Bye World";

  assert_true( compare_string( hello1, hello2 ) );
  assert_false( compare_string( hello1, bye ) );
}


static void
test_hash_string() {
  char hello1[] = "Hello World";
  char hello2[] = "Hello World";

  assert_true( hash_string( hello1 ) == hash_string( hello2 ) );
}


static void
test_compare_uint32() {
  uint32_t x = 123;
  uint32_t y = 123;
  uint32_t z = 321;

  assert_true( compare_uint32( ( void * ) &x, ( void * ) &y ) );
  assert_false( compare_uint32( ( void * ) &x, ( void * ) &z ) );
}


static void
test_hash_uint32() {
  uint32_t key = 123;

  assert_int_equal( 123, hash_uint32( ( void * ) &key ) );
}


static void
test_compare_datapath_id() {
  uint64_t x = 123;
  uint64_t y = 123;
  uint64_t z = 321;

  assert_true( compare_datapath_id( ( void * ) &x, ( void * ) &y ) );
  assert_false( compare_datapath_id( ( void * ) &x, ( void * ) &z ) );
}


static void
test_hash_datapath_id() {
  uint64_t x = 123;
  uint64_t y = 123;
  uint64_t z = 321;

  assert_true( hash_datapath_id( ( void * ) &x ) == hash_datapath_id( ( void * ) &y ) );
  assert_true( hash_datapath_id( ( void * ) &x ) != hash_datapath_id( ( void * ) &z ) );
}


static void
test_compare_mac() {
  uint8_t mac1[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  uint8_t mac2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

  assert_true( compare_mac( mac1, mac1 ) );
  assert_false( compare_mac( mac1, mac2 ) );
}


static void
test_hash_mac() {
  uint8_t mac1[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  uint8_t mac2[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

  assert_true( hash_mac( mac1 ) == hash_mac( mac2 ) );
}


static void
test_mac_to_uint64() {
  uint8_t mac1[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  uint8_t mac2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

  assert_true( mac_to_uint64( mac1 ) == 281474976710655ULL );
  assert_true( mac_to_uint64( mac2 ) == 0 );
}


static void
test_string_to_datapath_id() {
  uint64_t datapath_id;
  uint64_t expected_datapath_id = 18446744073709551615ULL;

  assert_true( string_to_datapath_id( "18446744073709551615", &datapath_id ) );
  assert_memory_equal( &datapath_id, &expected_datapath_id, sizeof( uint64_t ) );

  assert_false( string_to_datapath_id( "INVALID DATAPATH ID", &datapath_id ) );
}


static void
test_match_to_string() {
  char match_str[ MATCH_STRING_LENGTH ];

  {
    char expected_match_str[] = "all";
    oxm_matches *match = NULL;
    assert_true( match_to_string( match, match_str, sizeof( match_str ) ) );
    assert_string_equal( match_str, expected_match_str );
    match = create_oxm_matches();
    assert_true( match_to_string( match, match_str, sizeof( match_str ) ) );
    assert_string_equal( match_str, expected_match_str );
    delete_oxm_matches( match );
  }

  {
    char expected_match_str[] = 
      "in_port = 1, in_phy_port = 2, metadata = 0x0102030405060708, "
      "metadata = 0x0102030405060708/0xffffffff00000000, "
      "eth_dst = 01:02:03:04:05:06, eth_dst = 01:02:03:04:05:06/ff:ff:ff:00:00:00, "
      "eth_src = 01:02:03:04:05:06, eth_src = 01:02:03:04:05:06/ff:ff:ff:00:00:00, "
      "eth_type = 0x0800, vlan_vid = 0x0fa0, vlan_vid = 0x0fa0/0xff00, vlan_pcp = 0x03, "
      "ip_dscp = 0x04, ip_ecn = 0x05, ip_proto = 0x06, "
      "ipv4_src = 1.2.3.4, ipv4_src = 1.2.3.4/255.255.0.0, "
      "ipv4_dst = 1.2.3.4, ipv4_dst = 1.2.3.4/255.255.0.0, "
      "tcp_src = 1000, tcp_dst = 2000, udp_src = 3000, udp_dst = 4000, sctp_src = 5000, sctp_dst = 6000, "
      "icmpv4_type = 0x07, icmpv4_code = 0x08, arp_op = 0x0102, "
      "arp_spa = 1.2.3.4, arp_spa = 1.2.3.4/255.255.0.0, "
      "arp_tpa = 1.2.3.4, arp_tpa = 1.2.3.4/255.255.0.0, "
      "arp_sha = 01:02:03:04:05:06, arp_sha = 01:02:03:04:05:06/ff:ff:ff:00:00:00, "
      "arp_tha = 01:02:03:04:05:06, arp_tha = 01:02:03:04:05:06/ff:ff:ff:00:00:00, "
      "ipv6_src = 102:304:506:708:90a:b0c:d0e:f00, ipv6_src = 102:304:506:708:90a:b0c:d0e:f00/ffff:ffff:ffff:ffff::, "
      "ipv6_dst = 102:304:506:708:90a:b0c:d0e:f00, ipv6_dst = 102:304:506:708:90a:b0c:d0e:f00/ffff:ffff:ffff:ffff::, "
      "ipv6_flabel = 0x01020304, ipv6_flabel = 0x01020304/0xffff0000, icmpv6_type = 0x09, icmpv6_code = 0x0a, "
      "ipv6_nd_target = 102:304:506:708:90a:b0c:d0e:f00, ipv6_nd_sll = 01:02:03:04:05:06, ipv6_nd_tll = 01:02:03:04:05:06, "
      "mpls_label = 0x01020304, mpls_tc = 0x01, mpls_bos = 0x02, pbb_isid = 0x01020304, pbb_isid = 0x01020304/0xffff0000, "
      "tunnel_id = 0x0102030405060708, tunnel_id = 0x0102030405060708/0xffffffff00000000, "
      "ipv6_exthdr = 0x0102, ipv6_exthdr = 0x0102/0xff00";

    const uint16_t data_16bit = 0x0102;
    const uint32_t data_32bit = 0x01020304;
    const uint8_t data_48bit[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
    const uint64_t data_64bit = 0x0102030405060708;
    const struct in6_addr data_128bit = { { { 0x01, 0x02, 0x03, 0x04,
                                              0x05, 0x06, 0x07, 0x08,
                                              0x09, 0x0a, 0x0b, 0x0c,
                                              0x0d, 0x0e, 0x0f, 0x00 } } };

    const uint16_t mask_16bit = 0xff00;
    const uint32_t mask_32bit = 0xffff0000;
    const uint8_t mask_48bit[6] = { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 };
    const uint64_t mask_64bit = 0xffffffff00000000;
    const struct in6_addr mask_128bit = { { { 0xff, 0xff, 0xff, 0xff,
                                              0xff, 0xff, 0xff, 0xff,
                                              0x00, 0x00, 0x00, 0x00,
                                              0x00, 0x00, 0x00, 0x00 } } };

    const uint16_t nomask_16bit = 0x0000;
    const uint32_t nomask_32bit = 0x00000000;
    const uint8_t nomask_48bit[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    const uint64_t nomask_64bit = 0x0000000000000000;
    const struct in6_addr nomask_128bit = { { { 0x00, 0x00, 0x00, 0x00,
                                              0x00, 0x00, 0x00, 0x00,
                                              0x00, 0x00, 0x00, 0x00,
                                              0x00, 0x00, 0x00, 0x00 } } };

    uint8_t d_48bit[ OFP_ETH_ALEN ];
    uint8_t m_48bit[ OFP_ETH_ALEN ];
    uint8_t nm_48bit[ OFP_ETH_ALEN ];

    memcpy( d_48bit, data_48bit, sizeof( d_48bit ) );
    memcpy( m_48bit, mask_48bit, sizeof( m_48bit ) );
    memcpy( nm_48bit, nomask_48bit, sizeof( m_48bit ) );

    oxm_matches *match = create_oxm_matches();
    append_oxm_match_in_port( match, 1 );
    append_oxm_match_in_phy_port( match, 2 );
    append_oxm_match_metadata( match, data_64bit, nomask_64bit );
    append_oxm_match_metadata( match, data_64bit, mask_64bit );
    append_oxm_match_eth_dst( match, d_48bit, nm_48bit );
    append_oxm_match_eth_dst( match, d_48bit, m_48bit );
    append_oxm_match_eth_src( match, d_48bit, nm_48bit );
    append_oxm_match_eth_src( match, d_48bit, m_48bit );
    append_oxm_match_eth_type( match, 0x0800 );
    append_oxm_match_vlan_vid( match, 4000, nomask_16bit );
    append_oxm_match_vlan_vid( match, 4000, mask_16bit );
    append_oxm_match_vlan_pcp( match, 3 );
    append_oxm_match_ip_dscp( match, 4 );
    append_oxm_match_ip_ecn( match, 5 );
    append_oxm_match_ip_proto( match, 6 );
    append_oxm_match_ipv4_src( match, data_32bit, nomask_32bit );
    append_oxm_match_ipv4_src( match, data_32bit, mask_32bit );
    append_oxm_match_ipv4_dst( match, data_32bit, nomask_32bit );
    append_oxm_match_ipv4_dst( match, data_32bit, mask_32bit );
    append_oxm_match_tcp_src( match, 1000 );
    append_oxm_match_tcp_dst( match, 2000 );
    append_oxm_match_udp_src( match, 3000 );
    append_oxm_match_udp_dst( match, 4000 );
    append_oxm_match_sctp_src( match, 5000 );
    append_oxm_match_sctp_dst( match, 6000 );
    append_oxm_match_icmpv4_type( match, 7 );
    append_oxm_match_icmpv4_code( match, 8 );
    append_oxm_match_arp_op( match, data_16bit );
    append_oxm_match_arp_spa( match, data_32bit, nomask_32bit );
    append_oxm_match_arp_spa( match, data_32bit, mask_32bit );
    append_oxm_match_arp_tpa( match, data_32bit, nomask_32bit );
    append_oxm_match_arp_tpa( match, data_32bit, mask_32bit );
    append_oxm_match_arp_sha( match, d_48bit, nm_48bit );
    append_oxm_match_arp_sha( match, d_48bit, m_48bit );
    append_oxm_match_arp_tha( match, d_48bit, nm_48bit );
    append_oxm_match_arp_tha( match, d_48bit, m_48bit );
    append_oxm_match_ipv6_src( match, data_128bit, nomask_128bit );
    append_oxm_match_ipv6_src( match, data_128bit, mask_128bit );
    append_oxm_match_ipv6_dst( match, data_128bit, nomask_128bit );
    append_oxm_match_ipv6_dst( match, data_128bit, mask_128bit );
    append_oxm_match_ipv6_flabel( match, data_32bit, nomask_32bit );
    append_oxm_match_ipv6_flabel( match, data_32bit, mask_32bit );
    append_oxm_match_icmpv6_type( match, 9 );
    append_oxm_match_icmpv6_code( match, 0xa );
    append_oxm_match_ipv6_nd_target( match, data_128bit );
    append_oxm_match_ipv6_nd_sll( match, d_48bit );
    append_oxm_match_ipv6_nd_tll( match, d_48bit );
    append_oxm_match_mpls_label( match, data_32bit );
    append_oxm_match_mpls_tc( match, 1 );
    append_oxm_match_mpls_bos( match, 2 );
    append_oxm_match_pbb_isid( match, data_32bit, nomask_32bit );
    append_oxm_match_pbb_isid( match, data_32bit, mask_32bit );
    append_oxm_match_tunnel_id( match, data_64bit, nomask_64bit );
    append_oxm_match_tunnel_id( match, data_64bit, mask_64bit );
    append_oxm_match_ipv6_exthdr( match, data_16bit, nomask_16bit );
    append_oxm_match_ipv6_exthdr( match, data_16bit, mask_16bit );

    assert_true( match_to_string( match, match_str, sizeof( match_str ) ) );
    assert_string_equal( match_str, expected_match_str );

    delete_oxm_matches( match );
  }
}


static void
test_match_to_string_fails_with_insufficient_buffer() {
  char match_str[ 1 ];

  oxm_matches *match = create_oxm_matches();
  assert_false( match_to_string( match, match_str, sizeof( match_str ) ) );
  delete_oxm_matches( match );
}


static void
test_phy_port_to_string() {
  char phy_port_str[ 256 ];
  char expected_phy_port_str[] = "port_no = 1, hw_addr = 01:02:03:04:05:06, name = GbE 0/1, config = 0x1, state = 0x1, curr = 0x4820, advertised = 0xffff, supported = 0xffff, peer = 0xffff, curr_speed = 0x3e8, max_speed = 0x7d0";
  uint8_t hw_addr[ OFP_ETH_ALEN ] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
  uint32_t port_features = ( OFPPF_10MB_HD | OFPPF_10MB_FD | OFPPF_100MB_HD |
                            OFPPF_100MB_FD | OFPPF_1GB_HD | OFPPF_1GB_FD |
                            OFPPF_10GB_FD | OFPPF_40GB_FD | OFPPF_100GB_FD |
                            OFPPF_1TB_FD | OFPPF_OTHER | OFPPF_COPPER |
                            OFPPF_FIBER | OFPPF_AUTONEG | OFPPF_PAUSE |
                            OFPPF_PAUSE_ASYM );
  struct ofp_port phy_port;

  phy_port.port_no = 1;
  memcpy( phy_port.hw_addr, hw_addr, sizeof( phy_port.hw_addr ) );
  memset( phy_port.name, '\0', OFP_MAX_PORT_NAME_LEN );
  strncpy( phy_port.name, "GbE 0/1", OFP_MAX_PORT_NAME_LEN );
  phy_port.config = OFPPC_PORT_DOWN;
  phy_port.state = OFPPS_LINK_DOWN;
  phy_port.curr = ( OFPPF_1GB_FD | OFPPF_COPPER | OFPPF_PAUSE );
  phy_port.advertised = port_features;
  phy_port.supported = port_features;
  phy_port.peer = port_features;
  phy_port.curr_speed = 1000;
  phy_port.max_speed = 2000;

  assert_true( port_to_string( &phy_port, phy_port_str, sizeof( phy_port_str ) ) );
  assert_string_equal( phy_port_str, expected_phy_port_str );
}


static void
test_phy_port_to_string_fails_with_insufficient_buffer() {
  char phy_port_str[ 1 ];
  struct ofp_port phy_port;

  assert_false( port_to_string( &phy_port, phy_port_str, sizeof( phy_port_str ) ) );
}


static void
test_actions_to_string_with_action_output() {
  char str[ 128 ];
  char expected_str[] = "output: port=1 max_len=65535";
  struct ofp_action_output action;

  action.type = OFPAT_OUTPUT;
  action.len = sizeof( struct ofp_action_output );
  action.port = 1;
  action.max_len = 65535;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_set_field() {
  char str[ 128 ];
  char expected_str[] = "set_field: field=[tcp_src = 1000]";
  uint16_t len = ( uint16_t ) ( offsetof( struct ofp_action_set_field, field ) + sizeof( oxm_match_header ) + OXM_LENGTH( OXM_OF_TCP_SRC ) );
  uint16_t total_len = ( uint16_t ) ( len + PADLEN_TO_64( len ) );
  struct ofp_action_set_field *action = xcalloc( 1, total_len );
  oxm_match_header *oxm;
  uint16_t *oxm_val;

  action->type = OFPAT_SET_FIELD;
  action->len = total_len;
  oxm = ( oxm_match_header * ) action->field;
  *oxm = OXM_OF_TCP_SRC;
  oxm_val = ( uint16_t * ) ( oxm + 1 );
  *oxm_val = 1000;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) action, action->len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );

  xfree( action );
}


static void
test_actions_to_string_with_action_set_queue() {
  char str[ 128 ];
  char expected_str[] = "set_queue: queue_id=3";
  struct ofp_action_set_queue action;

  action.type = OFPAT_SET_QUEUE;
  action.len = sizeof( struct ofp_action_set_queue );
  action.queue_id = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_copy_ttl_out() {
  char str[ 128 ];
  char expected_str[] = "copy_ttl_out";
  struct ofp_action_header action;

  action.type = OFPAT_COPY_TTL_OUT;
  action.len = sizeof( struct ofp_action_header );

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_copy_ttl_in() {
  char str[ 128 ];
  char expected_str[] = "copy_ttl_in";
  struct ofp_action_header action;

  action.type = OFPAT_COPY_TTL_IN;
  action.len = sizeof( struct ofp_action_header );

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_set_mpls_ttl() {
  char str[ 128 ];
  char expected_str[] = "set_mpls_ttl: mpls_ttl=3";
  struct ofp_action_mpls_ttl action;

  action.type = OFPAT_SET_MPLS_TTL;
  action.len = sizeof( struct ofp_action_mpls_ttl );
  action.mpls_ttl = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_dec_mpls_ttl() {
  char str[ 128 ];
  char expected_str[] = "dec_mpls_ttl";
  struct ofp_action_header action;

  action.type = OFPAT_DEC_MPLS_TTL;
  action.len = sizeof( struct ofp_action_header );

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_push_vlan() {
  char str[ 128 ];
  char expected_str[] = "push_vlan: ethertype=0x3";
  struct ofp_action_push action;

  action.type = OFPAT_PUSH_VLAN;
  action.len = sizeof( struct ofp_action_push );
  action.ethertype = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_pop_vlan() {
  char str[ 128 ];
  char expected_str[] = "pop_vlan";
  struct ofp_action_header action;

  action.type = OFPAT_POP_VLAN;
  action.len = sizeof( struct ofp_action_header );

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_push_mpls() {
  char str[ 128 ];
  char expected_str[] = "push_mpls: ethertype=0x3";
  struct ofp_action_push action;

  action.type = OFPAT_PUSH_MPLS;
  action.len = sizeof( struct ofp_action_push );
  action.ethertype = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_pop_mpls() {
  char str[ 128 ];
  char expected_str[] = "pop_mpls: ethertype=0x3";
  struct ofp_action_pop_mpls action;

  action.type = OFPAT_POP_MPLS;
  action.len = sizeof( struct ofp_action_pop_mpls );
  action.ethertype = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_group() {
  char str[ 128 ];
  char expected_str[] = "group: group_id=0x3";
  struct ofp_action_group action;

  action.type = OFPAT_GROUP;
  action.len = sizeof( struct ofp_action_group );
  action.group_id = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_set_nw_ttl() {
  char str[ 128 ];
  char expected_str[] = "set_nw_ttl: nw_ttl=3";
  struct ofp_action_nw_ttl action;

  action.type = OFPAT_SET_NW_TTL;
  action.len = sizeof( struct ofp_action_nw_ttl );
  action.nw_ttl = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_dec_nw_ttl() {
  char str[ 128 ];
  char expected_str[] = "dec_nw_ttl";
  struct ofp_action_header action;

  action.type = OFPAT_DEC_NW_TTL;
  action.len = sizeof( struct ofp_action_header );

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_push_pbb() {
  char str[ 128 ];
  char expected_str[] = "push_pbb: ethertype=0x3";
  struct ofp_action_push action;

  action.type = OFPAT_PUSH_PBB;
  action.len = sizeof( struct ofp_action_push );
  action.ethertype = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_pop_pbb() {
  char str[ 128 ];
  char expected_str[] = "pop_pbb";
  struct ofp_action_header action;

  action.type = OFPAT_POP_PBB;
  action.len = sizeof( struct ofp_action_header );

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_action_experimenter() {
  char str[ 128 ];
  char expected_str[] = "experimenter: experimenter=0xdeadbeef";
  struct ofp_action_experimenter_header action;

  action.type = OFPAT_EXPERIMENTER;
  action.len = sizeof( struct ofp_action_experimenter_header );
  action.experimenter = 0xdeadbeef;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_undefined_action() {
  char str[ 128 ];
  char expected_str[] = "undefined: type=0xcafe";
  struct ofp_action_header action;

  action.type = 0xcafe;
  action.len = sizeof( struct ofp_action_header );

  assert_true( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_actions_to_string_with_multiple_actions() {
  char str[ 128 ];
  char expected_str[] = "output: port=1 max_len=65535, set_queue: queue_id=3";
  uint16_t actions_length = sizeof( struct ofp_action_output ) + sizeof( struct ofp_action_set_queue );
  void *actions = malloc( actions_length );
  memset( actions, 0, actions_length );
  struct ofp_action_output *output = actions;
  struct ofp_action_set_queue *set_queue = ( struct ofp_action_set_queue * ) ( ( char * ) actions + sizeof( struct ofp_action_output ) );

  output->type = OFPAT_OUTPUT;
  output->len = sizeof( struct ofp_action_output );
  output->port = 1;
  output->max_len = 65535;
  set_queue->type = OFPAT_SET_QUEUE;
  set_queue->len = sizeof( struct ofp_action_set_queue );
  set_queue->queue_id = 3;

  assert_true( actions_to_string( ( const struct ofp_action_header * ) actions, actions_length, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
  free( actions );
}


static void
test_actions_to_string_fails_with_insufficient_buffer() {
  char str[ 1 ];
  struct ofp_action_output action;

  action.type = OFPAT_OUTPUT;
  action.len = sizeof( struct ofp_action_output );
  action.port = 1;
  action.max_len = 65535;

  assert_false( actions_to_string( ( const struct ofp_action_header * ) &action, action.len, str, sizeof( str ) ) );
}


static void
test_instructions_to_string_goto_table() {
  char str[ 128 ];
  char expected_str[] = "goto_table: table_id=0x3";
  struct ofp_instruction_goto_table inst;

  inst.type = OFPIT_GOTO_TABLE;
  inst.len = sizeof( struct ofp_instruction_goto_table );
  inst.table_id = 3;

  assert_true( instructions_to_string( ( const struct ofp_instruction * ) &inst, inst.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_instructions_to_string_write_metadata() {
  char str[ 128 ];
  char expected_str[] = "write_metadata: metadata=0x3 metadata_mask=0x4";
  struct ofp_instruction_write_metadata inst;

  inst.type = OFPIT_WRITE_METADATA;
  inst.len = sizeof( struct ofp_instruction_write_metadata );
  inst.metadata = 3;
  inst.metadata_mask = 4;

  assert_true( instructions_to_string( ( const struct ofp_instruction * ) &inst, inst.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_instructions_to_string_write_actions() {
  char str[ 128 ];
  {
    char expected_str[] = "write_actions: actions=[set_queue: queue_id=3, output: port=1 max_len=65535]";
    uint16_t len = ( uint16_t ) ( offsetof( struct ofp_instruction_actions, actions ) + sizeof( struct ofp_action_set_queue ) + sizeof( struct ofp_action_output ) );
    struct ofp_instruction_actions *inst = xcalloc( 1, len );
    struct ofp_action_set_queue *set_queue = ( struct ofp_action_set_queue * ) inst->actions;
    struct ofp_action_output *output = ( struct ofp_action_output * ) ( set_queue + 1 );

    inst->type = OFPIT_WRITE_ACTIONS;
    inst->len = len;
    set_queue->type = OFPAT_SET_QUEUE;
    set_queue->len = sizeof( struct ofp_action_set_queue );
    set_queue->queue_id = 3;
    output->type = OFPAT_OUTPUT;
    output->len = sizeof( struct ofp_action_output );
    output->port = 1;
    output->max_len = 65535;

    assert_true( instructions_to_string( ( const struct ofp_instruction * ) inst, inst->len, str, sizeof( str ) ) );
    assert_string_equal( str, expected_str );

    xfree( inst );
  }

  {
    char expected_str[] = "write_actions: actions=[no action]";
    uint16_t len = ( uint16_t ) offsetof( struct ofp_instruction_actions, actions );
    struct ofp_instruction_actions *inst = xcalloc( 1, len );

    inst->type = OFPIT_WRITE_ACTIONS;
    inst->len = len;

    assert_true( instructions_to_string( ( const struct ofp_instruction * ) inst, inst->len, str, sizeof( str ) ) );
    assert_string_equal( str, expected_str );

    xfree( inst );
  }
}


static void
test_instructions_to_string_apply_actions() {
  char str[ 128 ];
  {
    char expected_str[] = "apply_actions: actions=[set_queue: queue_id=3, output: port=1 max_len=65535]";
    uint16_t len = ( uint16_t ) ( offsetof( struct ofp_instruction_actions, actions ) + sizeof( struct ofp_action_set_queue ) + sizeof( struct ofp_action_output ) );
    struct ofp_instruction_actions *inst = xcalloc( 1, len );
    struct ofp_action_set_queue *set_queue = ( struct ofp_action_set_queue * ) inst->actions;
    struct ofp_action_output *output = ( struct ofp_action_output * ) ( set_queue + 1 );

    inst->type = OFPIT_APPLY_ACTIONS;
    inst->len = len;
    set_queue->type = OFPAT_SET_QUEUE;
    set_queue->len = sizeof( struct ofp_action_set_queue );
    set_queue->queue_id = 3;
    output->type = OFPAT_OUTPUT;
    output->len = sizeof( struct ofp_action_output );
    output->port = 1;
    output->max_len = 65535;

    assert_true( instructions_to_string( ( const struct ofp_instruction * ) inst, inst->len, str, sizeof( str ) ) );
    assert_string_equal( str, expected_str );

    xfree( inst );
  }

  {
    char expected_str[] = "apply_actions: actions=[no action]";
    uint16_t len = ( uint16_t ) offsetof( struct ofp_instruction_actions, actions );
    struct ofp_instruction_actions *inst = xcalloc( 1, len );

    inst->type = OFPIT_APPLY_ACTIONS;
    inst->len = len;

    assert_true( instructions_to_string( ( const struct ofp_instruction * ) inst, inst->len, str, sizeof( str ) ) );
    assert_string_equal( str, expected_str );

    xfree( inst );
  }
}


static void
test_instructions_to_string_clear_actions() {
  char str[ 128 ];
  char expected_str[] = "clear_actions";
  struct ofp_instruction_actions inst;

  inst.type = OFPIT_CLEAR_ACTIONS;
  inst.len = sizeof( struct ofp_instruction_actions );

  assert_true( instructions_to_string( ( const struct ofp_instruction * ) &inst, inst.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_instructions_to_string_meter() {
  char str[ 128 ];
  char expected_str[] = "meter: meter_id=0x3";
  struct ofp_instruction_meter inst;

  inst.type = OFPIT_METER;
  inst.len = sizeof( struct ofp_instruction_meter );
  inst.meter_id = 3;

  assert_true( instructions_to_string( ( const struct ofp_instruction * ) &inst, inst.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_instructions_to_string_experimenter() {
  char str[ 128 ];
  char expected_str[] = "experimenter: experimenter=0x3";
  struct ofp_instruction_experimenter inst;

  inst.type = OFPIT_EXPERIMENTER;
  inst.len = sizeof( struct ofp_instruction_experimenter );
  inst.experimenter = 3;

  assert_true( instructions_to_string( ( const struct ofp_instruction * ) &inst, inst.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_instructions_to_string_with_undefined_instruction() {
  char str[ 128 ];
  char expected_str[] = "undefined: type=0xcafe";
  struct ofp_instruction inst;

  inst.type = 0xcafe;
  inst.len = sizeof( struct ofp_instruction );

  assert_true( instructions_to_string( ( const struct ofp_instruction * ) &inst, inst.len, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
}


static void
test_instructions_to_string_with_multiple_instructions() {
  char str[ 128 ];
  char expected_str[] = "goto_table: table_id=0x3, write_metadata: metadata=0x3 metadata_mask=0x4";
  uint16_t insts_length = sizeof( struct ofp_instruction_goto_table ) + sizeof( struct ofp_instruction_write_metadata );
  void *insts = malloc( insts_length );
  memset( insts, 0, insts_length );
  struct ofp_instruction_goto_table *goto_table = insts;
  struct ofp_instruction_write_metadata *write_metadata = ( struct ofp_instruction_write_metadata * ) ( ( char * ) insts + sizeof( struct ofp_instruction_goto_table ) );

  goto_table->type = OFPIT_GOTO_TABLE;
  goto_table->len = sizeof( struct ofp_instruction_goto_table );
  goto_table->table_id = 3;
  write_metadata->type = OFPIT_WRITE_METADATA;
  write_metadata->len = sizeof( struct ofp_instruction_write_metadata );
  write_metadata->metadata = 3;
  write_metadata->metadata_mask = 4;

  assert_true( instructions_to_string( ( const struct ofp_instruction * ) insts, insts_length, str, sizeof( str ) ) );
  assert_string_equal( str, expected_str );
  free( insts );
}


static void
test_instructions_to_string_fails_with_insufficient_buffer() {
  char str[ 1 ];
  struct ofp_instruction_goto_table inst;

  inst.type = OFPIT_GOTO_TABLE;
  inst.len = sizeof( struct ofp_instruction_goto_table );
  inst.table_id = 3;

  assert_false( instructions_to_string( ( const struct ofp_instruction * ) &inst, inst.len, str, sizeof( str ) ) );

}


static void
test_get_checksum_udp_packet() {
  ipv4_header_t ipv4_header;

  // Create a test packet.
  memset( &ipv4_header, 0, sizeof( ipv4_header ) );
  ipv4_header.version = 4;
  ipv4_header.ihl = 5;
  ipv4_header.tos = 0;
  ipv4_header.tot_len = htons( 0x004c );
  ipv4_header.id = htons( 0x48d8 );
  ipv4_header.frag_off = htons( 0 );
  ipv4_header.ttl = 0x80;
  ipv4_header.protocol = 0x11;
  ipv4_header.csum = 0;
  ipv4_header.saddr = htonl( 0x0a3835af );
  ipv4_header.daddr = htonl( 0x0a3837ff );

  uint16_t checksum = get_checksum( ( uint16_t * ) &ipv4_header,
                                    sizeof( ipv4_header ) );
  assert_int_equal( checksum, 0xab6f );
}


static void
test_get_checksum_icmp_packet() {
  ipv4_header_t ipv4_header;

  // Create a test packet.
  memset( &ipv4_header, 0, sizeof( ipv4_header ) );
  ipv4_header.version = 4;
  ipv4_header.ihl = 5;
  ipv4_header.tos = 0;
  ipv4_header.tot_len = htons( 0x0054 );
  ipv4_header.id = htons( 0xaec3 );
  ipv4_header.frag_off = htons( 0 );
  ipv4_header.ttl = 0x40;
  ipv4_header.protocol = 0x01;
  ipv4_header.csum = 0;
  ipv4_header.saddr = htonl( 0xc0a8642b );
  ipv4_header.daddr = htonl( 0xc0a8642c );

  uint16_t checksum = get_checksum( ( uint16_t * ) &ipv4_header,
                                    sizeof( ipv4_header ) );
  assert_int_equal( checksum, 0x3d82 );
}


/********************************************************************************
 * Run tests.
 ********************************************************************************/

int
main() {
  const UnitTest tests[] = {
    unit_test_setup_teardown( test_die, setup, teardown ),

    unit_test( test_hash_core ),

    unit_test( test_compare_string ),
    unit_test( test_hash_string ),

    unit_test( test_compare_uint32 ),
    unit_test( test_hash_uint32 ),

    unit_test( test_compare_datapath_id ),
    unit_test( test_hash_datapath_id ),

    unit_test( test_compare_mac ),
    unit_test( test_hash_mac ),
    unit_test( test_mac_to_uint64 ),

    unit_test( test_string_to_datapath_id ),

    unit_test( test_match_to_string ),
    unit_test( test_match_to_string_fails_with_insufficient_buffer ),

    unit_test( test_phy_port_to_string ),
    unit_test( test_phy_port_to_string_fails_with_insufficient_buffer ),

    unit_test( test_actions_to_string_with_action_output ),
    unit_test( test_actions_to_string_with_action_set_field ),
    unit_test( test_actions_to_string_with_action_set_queue ),
    unit_test( test_actions_to_string_with_action_copy_ttl_out ),
    unit_test( test_actions_to_string_with_action_copy_ttl_in ),
    unit_test( test_actions_to_string_with_action_set_mpls_ttl ),
    unit_test( test_actions_to_string_with_action_dec_mpls_ttl ),
    unit_test( test_actions_to_string_with_action_push_vlan ),
    unit_test( test_actions_to_string_with_action_pop_vlan ),
    unit_test( test_actions_to_string_with_action_push_mpls ),
    unit_test( test_actions_to_string_with_action_pop_mpls ),
    unit_test( test_actions_to_string_with_action_group ),
    unit_test( test_actions_to_string_with_action_set_nw_ttl ),
    unit_test( test_actions_to_string_with_action_dec_nw_ttl ),
    unit_test( test_actions_to_string_with_action_push_pbb ),
    unit_test( test_actions_to_string_with_action_pop_pbb ),
    unit_test( test_actions_to_string_with_action_experimenter ),
    unit_test( test_actions_to_string_with_undefined_action ),
    unit_test( test_actions_to_string_with_multiple_actions ),
    unit_test( test_actions_to_string_fails_with_insufficient_buffer ),

    unit_test( test_instructions_to_string_goto_table ),
    unit_test( test_instructions_to_string_write_metadata ),
    unit_test( test_instructions_to_string_write_actions ),
    unit_test( test_instructions_to_string_apply_actions ),
    unit_test( test_instructions_to_string_clear_actions ),
    unit_test( test_instructions_to_string_meter ),
    unit_test( test_instructions_to_string_experimenter ),
    unit_test( test_instructions_to_string_with_undefined_instruction ),
    unit_test( test_instructions_to_string_with_multiple_instructions ),
    unit_test( test_instructions_to_string_fails_with_insufficient_buffer ),

    unit_test( test_get_checksum_udp_packet ),
    unit_test( test_get_checksum_icmp_packet ),
  };
  setup_leak_detector();
  return run_tests( tests );
}


/*
 * Local variables:
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * End:
 */