//-----------------------------------------------------------------------------
// file:  pacmantest.cc
// date:  Wed May 07 10:48:11 PDT 2003 
// Author: Martin Casado, Norman Franke
// 
// Description:
//
// Uber simple test suite for pacman
//
// TODO: 
//   - add tcp_options to test suite
//
// ----------------------------------------------------------------------------
//
//    Copyright 2003. The Regents of the University of California.
//    All rights reserved.
//
//    This work was produced at the University of California, Lawrence Livermore
//    National Laboratory (UC LLNL) under contract no. W-7405-ENG-48 (Contract
//    48) between the U.S. Department of Energy (DOE) and The Regents of the
//    University of California (University) for the operation of UC LLNL.
//    Copyright is reserved to the University for purposes of controlled
//    dissemination, commercialization through formal licensing, or other
//    disposition under terms of Contract 48; DOE policies, regulations and
//    orders; and U.S. statutes. The rights of the Federal Government are
//    reserved under Contract 48 subject to the restrictions agreed upon by the
//    DOE and University as allowed under DOE Acquisition Letter 91-7.
//
//    DISCLAIMER
//    This software was prepared as an account of work sponsored by an
//    agency of the United States Government. Neither the United States
//    Government nor the University of California nor any of their employees,
//    makes any warranty, express or implied, or assumes any liability or
//    responsibility for the accuracy, completeness, or usefulness of any
//    information, apparatus, product, or process disclosed, or represents that
//    its use would not infringe protectedly-owned rights. Reference herein to any
//    specific commercial products, process, or service by trade name, trademark,
//    manufacturer, or otherwise, does not necessarily constitute or imply its
//    endorsement, recommendation, or favoring by the United States Government or
//    the University of California. The views and opinions of authors expressed
//    herein do not necessarily represent those of the United States
//    Government or the University of California, and shall not be used for
//    advertising or product endorsement purposes.
//
//-----------------------------------------------------------------------------

#include "ip.hh"
#include "udp.hh"
#include "tcp.hh"
#include "icmp.hh"
#include "ip_options.hh"
#include "tcp_options.hh"
#include "dns.hh"
#include "ethernet.hh"
#include "pcap_writer.hh"
#include "arp.hh"
#include <iostream>

using namespace pacman;
using namespace boost;
using namespace std;

void basic_eth_test(void);
void basic_ip_options_test(void);
void basic_ip_test(void);
void basic_hdr_test(void);
void basic_udp_test(void);
void basic_tcp_test(void);
void basic_arp_test(void);

// ----------------------------------------------------------------------------
int main(int argc,char **argv)
{ 
    basic_eth_test();
    basic_ip_options_test();
    basic_ip_test();
    basic_hdr_test();
    basic_udp_test();
    basic_tcp_test();
    basic_arp_test();

    return 0;
} // -- main
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
void basic_eth_test(void)
{
    ethernet<_> eth_;
    ethernet< ip<_,_> > ethip_;

    assert( ((int)eth_.type == ethernet_base::DEFTYPE) );
    assert( ((int)ethip_.type == ethernet_base::IP) );
    assert( sizeof(ethernet<_>) == 14 );
    assert( sizeof(ethernet<bool>) == 14 + sizeof(bool) );
}
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
void basic_hdr_test(void)
{
    assert ( (hdr< ip<_,_> >::len == 20) );
    assert ( (hdr< ip<_,bool> >::len == 20) );
    assert ( (hdr< ip<_,uint16_t> >::len == 20) );
    assert ( (hdr< ip<_,uint32_t> >::len == 20) );
    assert ( (hdr< ip<bool,_> > :: len == 24) ) ;
    assert ( (hdr< ip<ip_options<bool>,_> > :: len == 24) ) ;
    assert ( (hdr< ip<uint16_t,_> > :: len == 24) ) ;
    assert ( (hdr< ip<ip_options<uint16_t>,_> > :: len == 24) ) ;
    assert ( (hdr< ip<uint32_t,_> > :: len == 24) ) ;
    assert ( (hdr< ip<ip_options<uint32_t>,_> > :: len == 24) ) ;
    assert ( (hdr< ip<bool,int> > :: len == 24) ) ;
    assert ( (hdr< ip<ip_options<bool>,int> > :: len == 24) ) ;
    assert ( (hdr< ip<bool,tcp<_> > > :: len == 24) ) ;
    assert ( (hdr< ip<ip_options<bool>,tcp<_> > > :: len == 24) ) ;
    assert ( (hdr< ip<_,tcp<_> > > :: len == 20) ) ;
    assert ( (hdr< ip<bool,udp<_> > > :: len == 24) ) ;
    assert ( (hdr< ip<ip_options<bool>,udp<_> > > :: len == 24) ) ;
    assert ( (hdr< ip<_,udp<_> > > :: len == 20) ) ;
}
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
void basic_ip_options_test(void)
{
    // -- Check to see that the filler works correctly
    assert (sizeof(ip_options<bool>)     == 4);
    assert (sizeof(ip_options<uint16_t>) == 4);
    assert (sizeof(ip_options<uint32_t>) == 4);
    assert (sizeof(ip_options<char[5]>)  == 8);
    assert (sizeof(ip_options<char[6]>)  == 8);
    assert (sizeof(ip_options<char[9]>)  == 12);
}
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
void 
basic_ip_test()
{
    // -- make sure sizes are correct
    assert( sizeof(ip<_,_>) == 20 );
    assert( sizeof(ip<_,int>) == sizeof(ip<_,_>) + sizeof(int) );


    // -- Is the protocol set correctly?
    ip<_,_>   ip__;
    ip<_,int> ip_i_;
    ip<_,tcp<> > ip_t_;
    ip<_,udp<> > ip_u_;
    assert ( (int)ip__.protocol == ip_base::DEFPROTO );
    assert ( (int)ip_i_.protocol == ip_base::DEFPROTO );
    assert ( (int)ip_u_.protocol == ip_base::UDP );
    assert ( (int)ip_t_.protocol == ip_base::TCP );

    // -- How about the header length? 
    ip<ip_options<bool>,_> ipb__;
    ip<ip_options<uint16_t>,_> ips__;
    ip<ip_options<uint32_t>,_> ipi__;
    ip<ip_options<char[5]>,_> ipc__;
    assert ( (int) ip__.ihl  == 5);
    assert ( (int) ip_i_.ihl == 5);
    assert ( (int) ip_t_.ihl == 5);
    assert ( (int) ipb__.ihl == 6 );
    assert ( (int) ips__.ihl == 6 );
    assert ( (int) ipi__.ihl == 6 );
    assert ( (int) ipc__.ihl == 7 );

    // -- Total Length?
    assert ( ntohs( (int) ip__.tot_len) == 20);
    assert ( ntohs( (int) ip_i_.tot_len) == ( 20 + sizeof(int) ) );
    assert ( ntohs( (int) ipi__.tot_len) == ( 20 + sizeof(int) ) );

} // -- basic_ip_test
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
void basic_udp_test(void)
{
    ip< _, udp<> > udp_0;
    ip< _, udp< uint8_t[64] > > udp_64;

    assert ( sizeof(udp_0) == 20+8 );
    assert ( sizeof(udp_64) == 20+8+64 );
    //assert ( udp_0.data.len == 8 );
    //assert ( udp_64.data.len == 8+64 );

    //pcap_writer writer("test",0);

    //writer.write(udp_0);
    //checksum(udp_0);

    //writer.write(udp_0);
    //writer.close();
} // -- basic_udp_test
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
void basic_tcp_test(void)
{
    ip< _, tcp< _, _ > > tcp_0;
    ip< _, tcp< _, uint8_t[64] > > tcp_64;
    ip< _, tcp< tcp_options<_>, uint8_t[64] > > tcp_64o0;
    ip< _, tcp< tcp_options< tcp_mss< > >, uint8_t[64] > > tcp_64o1;

    assert ( sizeof(tcp_0) == 20+20 );
    assert ( sizeof(tcp_64) == 20+20+64 );
    assert ( sizeof(tcp_64o0) == 20+20+64+4 );
    assert ( sizeof(tcp_64o1) == 20+20+64+4 );
    assert ( tcp_0.data.len() == 20 );
    assert ( tcp_64.data.len() == 20 ); // doesn't include data
} // -- basic_tcp_test
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
void basic_arp_test(void)
{
    arp <> arp1;
    arp < arp<>::REPLY > arp2;

    // -- preprocessor mistakes , int ip<_,_> for argument delimeter  ...
    //    lame preprocessor lame, lame lame
    unsigned int ipaddr_size = ip<_,_>::ADDRLEN;

    assert ( sizeof(arp1) == 2*ethernet<_>::ADDRLEN + 2*ipaddr_size + 8 ); 
    assert ( arp1.hrd = arp<>::ETHER ); 
    assert ( arp1.pro = ethernet<_>::IP ); 
    assert ( arp1.hln = ethernet<_>::ADDRLEN );
    assert ( arp1.pln = ipaddr_size );
    assert ( arp1.op  = arp<>::REQUEST );


    ethernet < arp<> > eth_arp;

    int res = boost::is_base_and_derived<ether_type, arp<> >::value;
    assert ( res ); // -- is arp derived from ether_type?
    res =  ether_type_if_true < 1, arp<>, 0 > ::val;
    assert ( res = ethernet<>::ARP ); 
    assert ( eth_arp.type == ethernet<>::ARP );
}
// ----------------------------------------------------------------------------
