/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2009 University of Washington
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "test.h"
#include "object.h"
#include "boolean.h"
#include "integer.h"
#include "uinteger.h"
#include "enum.h"
#include "string.h"
#include "random-variable.h"
#include "double.h"
#include "object-vector.h"
#include "traced-value.h"
#include "callback.h"
#include "trace-source-accessor.h"
#include "pointer.h"

namespace ns3 {

class ValueClassTest 
{
public:
  ValueClassTest () {}
private:
  int m_v;
};
bool operator != (const ValueClassTest &a, const ValueClassTest &b)
{
  return true;
}
std::ostream & operator << (std::ostream &os, ValueClassTest v)
{
  return os;
}
std::istream & operator >> (std::istream &is, ValueClassTest &v)
{
  return is;
}
ATTRIBUTE_HELPER_HEADER (ValueClassTest);
ATTRIBUTE_HELPER_CPP (ValueClassTest);

class Derived : public Object
{
public:
  static TypeId GetTypeId (void) {
    static TypeId tid = TypeId ("Derived")
      .SetParent<Object> ()
      ;
    return tid;
  }
};

class AttributeObjectTest : public Object
{
public:
  enum Test_e {
    TEST_A,
    TEST_B,
    TEST_C
  };
  static TypeId GetTypeId (void) {
    static TypeId tid = TypeId ("ns3::AttributeObjectTest")
      .SetParent<Object> ()
      .HideFromDocumentation ()
      .AddAttribute ("TestBoolName", "help text",
		     BooleanValue (false),
		     MakeBooleanAccessor (&AttributeObjectTest::m_boolTest),
		     MakeBooleanChecker ())
      .AddAttribute ("TestBoolA", "help text",
		     BooleanValue (false),
		     MakeBooleanAccessor (&AttributeObjectTest::DoSetTestB,
					   &AttributeObjectTest::DoGetTestB),
		     MakeBooleanChecker ())
      .AddAttribute ("TestInt16", "help text",
		     IntegerValue (-2),
		     MakeIntegerAccessor (&AttributeObjectTest::m_int16),
		     MakeIntegerChecker<int16_t> ())
      .AddAttribute ("TestInt16WithBounds", "help text",
		     IntegerValue (-2),
		     MakeIntegerAccessor (&AttributeObjectTest::m_int16WithBounds),
		     MakeIntegerChecker<int16_t> (-5, 10))
      .AddAttribute ("TestInt16SetGet", "help text",
		     IntegerValue (6),
		     MakeIntegerAccessor (&AttributeObjectTest::DoSetInt16,
				       &AttributeObjectTest::DoGetInt16),
		     MakeIntegerChecker<int16_t> ())
      .AddAttribute ("TestUint8", "help text",
		     UintegerValue (1),
		     MakeUintegerAccessor (&AttributeObjectTest::m_uint8),
		     MakeUintegerChecker<uint8_t> ())
      .AddAttribute ("TestEnum", "help text",
		     EnumValue (TEST_A),
		     MakeEnumAccessor (&AttributeObjectTest::m_enum),
		     MakeEnumChecker (TEST_A, "TestA",
				      TEST_B, "TestB",
				      TEST_C, "TestC"))
      .AddAttribute ("TestRandom", "help text",
		     RandomVariableValue (ConstantVariable (1.0)),
		     MakeRandomVariableAccessor (&AttributeObjectTest::m_random),
		     MakeRandomVariableChecker ())
      .AddAttribute ("TestFloat", "help text",
		     DoubleValue (-1.1),
		     MakeDoubleAccessor (&AttributeObjectTest::m_float),
		     MakeDoubleChecker<float> ())
      .AddAttribute ("TestVector1", "help text",
		     ObjectVectorValue (),
		     MakeObjectVectorAccessor (&AttributeObjectTest::m_vector1),
		     MakeObjectVectorChecker<Derived> ())
      .AddAttribute ("TestVector2", "help text",
		     ObjectVectorValue (),
		     MakeObjectVectorAccessor (&AttributeObjectTest::DoGetVectorN,
						&AttributeObjectTest::DoGetVector),
		     MakeObjectVectorChecker<Derived> ())
      .AddAttribute ("IntegerTraceSource1", "help text",
		     IntegerValue (-2),
		     MakeIntegerAccessor (&AttributeObjectTest::m_intSrc1),
		     MakeIntegerChecker<int8_t> ())
      .AddAttribute ("IntegerTraceSource2", "help text",
		     IntegerValue (-2),
		     MakeIntegerAccessor (&AttributeObjectTest::DoSetIntSrc,
					  &AttributeObjectTest::DoGetIntSrc),
		     MakeIntegerChecker<int8_t> ())
      .AddAttribute ("ValueClassSource", "help text",
		     ValueClassTestValue (ValueClassTest ()),
		     MakeValueClassTestAccessor (&AttributeObjectTest::m_valueSrc),
		     MakeValueClassTestChecker ())
      .AddTraceSource ("Source1", "help test",
		       MakeTraceSourceAccessor (&AttributeObjectTest::m_intSrc1))
      .AddTraceSource ("Source2", "help text",
		       MakeTraceSourceAccessor (&AttributeObjectTest::m_cb))
      .AddTraceSource ("ValueSource", "help text",
		       MakeTraceSourceAccessor (&AttributeObjectTest::m_valueSrc))
      .AddAttribute ("Pointer", "help text",
                     PointerValue (),
                     MakePointerAccessor (&AttributeObjectTest::m_ptr),
                     MakePointerChecker<Derived> ())
      .AddAttribute ("Callback", "help text",
                     CallbackValue (),
                     MakeCallbackAccessor (&AttributeObjectTest::m_cbValue),
                     MakeCallbackChecker ())
      ;
        
    return tid;
  }

  void AddToVector1 (void) {m_vector1.push_back (CreateObject<Derived> ());}
  void AddToVector2 (void) {m_vector2.push_back (CreateObject<Derived> ());}
  void InvokeCb (double a, int b, float c) {m_cb (a,b,c);}

  void InvokeCbValue (int8_t a)
  {
    if (!m_cbValue.IsNull ()) {
        m_cbValue (a);
      }
  }

private:
  void DoSetTestB (bool v) {m_boolTestA = v;}
  bool DoGetTestB (void) const {return m_boolTestA;}
  int16_t DoGetInt16 (void) const {return m_int16SetGet;}
  void DoSetInt16 (int16_t v) {m_int16SetGet = v;}
  uint32_t DoGetVectorN (void) const {return m_vector2.size ();}
  Ptr<Derived> DoGetVector (uint32_t i) const {return m_vector2[i];}
  bool DoSetIntSrc (int8_t v) {m_intSrc2 = v; return true;}
  int8_t DoGetIntSrc (void) const {return m_intSrc2;}

  bool m_boolTestA;
  bool m_boolTest;
  int16_t m_int16;
  int16_t m_int16WithBounds;
  int16_t m_int16SetGet;
  uint8_t m_uint8;
  float m_float;
  enum Test_e m_enum;
  RandomVariable m_random;
  std::vector<Ptr<Derived> > m_vector1;
  std::vector<Ptr<Derived> > m_vector2;
  Callback<void,int8_t> m_cbValue;
  TracedValue<int8_t> m_intSrc1;
  TracedValue<int8_t> m_intSrc2;
  TracedCallback<double, int, float> m_cb;
  TracedValue<ValueClassTest> m_valueSrc;
  Ptr<Derived> m_ptr;
};

NS_OBJECT_ENSURE_REGISTERED (AttributeObjectTest);

// ===========================================================================
// Test case template used for generic Attribute Value types -- used to make 
// sure that Attributes work as expected.
// ===========================================================================
template <typename T>
class AttributeTestCase : public TestCase
{
public:
  AttributeTestCase (std::string description);
  virtual ~AttributeTestCase ();

private:
  virtual bool DoRun (void);

  bool CheckGetCodePaths (Ptr<Object> p, std::string attributeName, std::string expectedString, T expectedValue);
};

template <typename T>
AttributeTestCase<T>::AttributeTestCase (std::string description)
  : TestCase (description)
{
}

template <typename T>
AttributeTestCase<T>::~AttributeTestCase ()
{
}

template <typename T> bool
AttributeTestCase<T>::CheckGetCodePaths (
  Ptr<Object> p, 
  std::string attributeName,
  std::string expectedString,
  T expectedValue)
{
  StringValue stringValue;
  T actualValue;

  //
  // Get an Attribute value through its StringValue representation.
  //
  bool ok = p->GetAttributeFailSafe (attributeName.c_str (), stringValue);
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not GetAttributeFailSafe() " << attributeName << " from Object");
  NS_TEST_ASSERT_MSG_EQ (stringValue.Get (), expectedString, "Got unexpected StringValue representation");

  //
  // Get the existing boolean value through its particular type representation.
  //
  ok = p->GetAttributeFailSafe (attributeName.c_str (), actualValue);
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not GetAttributeFailSafe() " << attributeName << " from Object");
  NS_TEST_ASSERT_MSG_EQ (actualValue.Get (), expectedValue.Get (), "Got unexpected Value from Object");

  return GetErrorStatus ();
}

// ===========================================================================
// The actual Attribute type test cases are specialized for each Attribute type
// ===========================================================================
template <> bool
AttributeTestCase<BooleanValue>::DoRun (void)
{
  AttributeList attrs;
  Ptr<AttributeObjectTest> p;
  bool ok;

  //
  // Test setting a boolean via string representation using AttributeList.
  //
  ok = attrs.SetFailSafe ("ns3::AttributeObjectTest::TestBoolName", StringValue ("false"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetFailSafe() \"ns3::AttributeObjectTest::TestBoolName\" into AttributeList");

  //
  // Create an object using that attribute list (which should therefore have the
  // boolean from above set to false.
  //
  p = CreateObjectWithAttributes<AttributeObjectTest> (attrs);
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObjectWithAttributes");

  ok = CheckGetCodePaths (p, "TestBoolName", "false", BooleanValue (false));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly from CreateObjectWithAttributes");

  //
  // Set the default value of the BooleanValue and create an object.  The new
  // default value should stick.
  //
  AttributeList::GetGlobal ()->SetFailSafe ("ns3::AttributeObjectTest::TestBoolName", StringValue ("true"));
  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  ok = CheckGetCodePaths (p, "TestBoolName", "true", BooleanValue (true));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by default value");

  //
  // Set the default value of the BooleanValue the other way and create an object.
  // The new default value should stick.
  //
  AttributeList::GetGlobal ()->SetFailSafe ("ns3::AttributeObjectTest::TestBoolName", StringValue ("false"));

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  ok = CheckGetCodePaths (p, "TestBoolName", "false", BooleanValue (false));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not et properly by default value");

  //
  // Set the BooleanValue Attribute to true via SetAttributeFailSafe path.
  //
  ok = p->SetAttributeFailSafe("TestBoolName", StringValue ("true"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() \"TestBoolName\" to true");

  ok = CheckGetCodePaths (p, "TestBoolName", "true", BooleanValue (true));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via StringValue");

  //
  // Set the BooleanValue to false via SetAttributeFailSafe path.
  //
  ok = p->SetAttributeFailSafe("TestBoolName", StringValue ("false"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() \"TestBoolName\" to false");

  ok = CheckGetCodePaths (p, "TestBoolName", "false", BooleanValue (false));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via StringValue");

  //
  // Create an object using individually provided StringValue Attribute.
  //
  p = CreateObjectWithAttributes<AttributeObjectTest> ("TestBoolName", StringValue ("true"));
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObjectWithAttributes");

  ok = CheckGetCodePaths (p, "TestBoolName", "true", BooleanValue (true));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by CreateObjectWithAttributes() with StringValue");

  //
  // Create an object using individually provided BooleanValue Attribute.
  //
  p = CreateObjectWithAttributes<AttributeObjectTest> ("TestBoolName", BooleanValue (false));
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObjectWithAttributes");

  ok = CheckGetCodePaths (p, "TestBoolName", "false", BooleanValue (false));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by CreateObjectWithAttributes() with BooleanValue");

  //
  // The previous object-based tests checked access directly.  Now check through
  // setter and getter.  The code here looks the same, but the underlying
  // attribute is declared differently in the object.  First make sure we can set
  // to true.
  //
  ok = p->SetAttributeFailSafe("TestBoolA", StringValue ("true"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() a boolean value to true");

  ok = CheckGetCodePaths (p, "TestBoolA", "true", BooleanValue (true));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() (getter/setter) via StringValue");

  //
  // Now Set the BooleanValue to false via the setter.
  //
  ok = p->SetAttributeFailSafe("TestBoolA", StringValue ("false"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() a boolean value to false");

  ok = CheckGetCodePaths (p, "TestBoolA", "false", BooleanValue (false));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() (getter/setter) via StringValue");

  return GetErrorStatus ();
}

template <> bool
AttributeTestCase<IntegerValue>::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // When the object is first created, the Attribute should have the default 
  // value.
  //
  ok = CheckGetCodePaths (p, "TestInt16", "-2", IntegerValue (-2));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by default value");

  //
  // Set the Attribute to a negative value through a StringValue.
  //
  ok = p->SetAttributeFailSafe("TestInt16", StringValue ("-5"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via StringValue to -5");

  ok = CheckGetCodePaths (p, "TestInt16", "-5", IntegerValue (-5));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via StringValue");

  //
  // Set the Attribute to a positive value through a StringValue.
  //
  ok = p->SetAttributeFailSafe("TestInt16", StringValue ("+2"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via StringValue to +2");

  ok = CheckGetCodePaths (p, "TestInt16", "2", IntegerValue (2));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via StringValue");

  //
  // Set the Attribute to the most negative value of the signed 16-bit range.
  //
  ok = p->SetAttributeFailSafe("TestInt16", StringValue ("-32768"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via StringValue to -32768");

  ok = CheckGetCodePaths (p, "TestInt16", "-32768", IntegerValue (-32768));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() (most negative) via StringValue");

  //
  // Try to set the Attribute past the most negative value of the signed 16-bit
  // range and make sure the underlying attribute is unchanged.
  //
  ok = p->SetAttributeFailSafe("TestInt16", StringValue ("-32769"));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via StringValue to -32769");

  ok = CheckGetCodePaths (p, "TestInt16", "-32768", IntegerValue (-32768));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Error in SetAttributeFailSafe() but value changes");

  //
  // Set the Attribute to the most positive value of the signed 16-bit range.
  //
  ok = p->SetAttributeFailSafe("TestInt16", StringValue ("32767"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via StringValue to 32767");

  ok = CheckGetCodePaths (p, "TestInt16", "32767", IntegerValue (32767));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() (most positive) via StringValue");

  //
  // Try to set the Attribute past the most positive value of the signed 16-bit
  // range and make sure the underlying attribute is unchanged.
  //
  ok = p->SetAttributeFailSafe("TestInt16", StringValue ("32768"));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via StringValue to 32768");

  ok = CheckGetCodePaths (p, "TestInt16", "32767", IntegerValue (32767));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Error in SetAttributeFailSafe() but value changes");

  //
  // Attributes can have limits other than the intrinsic limits of the
  // underlying data types.  These limits are specified in the Object.
  //
  ok = p->SetAttributeFailSafe("TestInt16WithBounds", IntegerValue (10));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to 10");

  ok = CheckGetCodePaths (p, "TestInt16WithBounds", "10", IntegerValue (10));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() (positive limit) via StringValue");

  //
  // Set the Attribute past the positive limit.
  //
  ok = p->SetAttributeFailSafe("TestInt16WithBounds", IntegerValue (11));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via IntegerValue to 11");

  ok = CheckGetCodePaths (p, "TestInt16WithBounds", "10", IntegerValue (10));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Error in SetAttributeFailSafe() but value changes");

  //
  // Set the Attribute at the negative limit.
  //
  ok = p->SetAttributeFailSafe("TestInt16WithBounds", IntegerValue (-5));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to -5");

  ok = CheckGetCodePaths (p, "TestInt16WithBounds", "-5", IntegerValue (-5));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() (negative limit) via StringValue");

  //
  // Set the Attribute past the negative limit.
  //
  ok = p->SetAttributeFailSafe("TestInt16WithBounds", IntegerValue (-6));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via IntegerValue to -6");

  ok = CheckGetCodePaths (p, "TestInt16WithBounds", "-5", IntegerValue (-5));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Error in SetAttributeFailSafe() but value changes");

  return GetErrorStatus ();
}

template <> bool
AttributeTestCase<UintegerValue>::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // When the object is first created, the Attribute should have the default 
  // value.
  //
  ok = CheckGetCodePaths (p, "TestUint8", "1", UintegerValue (1));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by default value");;

  //
  // Set the Attribute to zero.
  //
  ok = p->SetAttributeFailSafe("TestUint8", UintegerValue (0));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() to 0");

  ok = CheckGetCodePaths (p, "TestUint8", "0", UintegerValue (0));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via StringValue");

  //
  // Set the Attribute to the most positive value of the unsigned 8-bit range.
  //
  ok = p->SetAttributeFailSafe("TestUint8", UintegerValue (255));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() to 255");

  ok = CheckGetCodePaths (p, "TestUint8", "255", UintegerValue (255));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() (positive limit) via UintegerValue");

  //
  // Try and set the Attribute past the most positive value of the unsigned 
  // 8-bit range.
  //
  ok = p->SetAttributeFailSafe("TestUint8", UintegerValue (256));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() to 256");

  ok = CheckGetCodePaths (p, "TestUint8", "255", UintegerValue (255));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Error in SetAttributeFailSafe() but value changes");

  //
  // Set the Attribute to the most positive value of the unsigned 8-bit range
  // through a StringValue.
  //
  ok = p->SetAttributeFailSafe("TestUint8", StringValue ("255"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via StringValue to 255");

  ok = CheckGetCodePaths (p, "TestUint8", "255", UintegerValue (255));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via StringValue");

  //
  // Try and set the Attribute past the most positive value of the unsigned
  // 8-bit range through a StringValue.
  //
  ok = p->SetAttributeFailSafe("TestUint8", StringValue ("256"));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via StringValue to 256");

  ok = CheckGetCodePaths (p, "TestUint8", "255", UintegerValue (255));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Error in SetAttributeFailSafe() but value changes");

  //
  // Try to set the Attribute to a negative StringValue.
  //
  ok = p->SetAttributeFailSafe("TestUint8", StringValue ("-1"));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via StringValue to -1");

  return GetErrorStatus ();
}

template <> bool
AttributeTestCase<DoubleValue>::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // When the object is first created, the Attribute should have the default 
  // value.
  //
  ok = CheckGetCodePaths (p, "TestFloat", "-1.1", DoubleValue ((float)-1.1));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by default value");

  //
  // Set the Attribute.
  //
  ok = p->SetAttributeFailSafe("TestFloat", DoubleValue ((float)2.3));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() to 2.3");

  ok = CheckGetCodePaths (p, "TestFloat", "2.3", DoubleValue ((float)2.3));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via DoubleValue");

  return GetErrorStatus ();
}

template <> bool
AttributeTestCase<EnumValue>::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // When the object is first created, the Attribute should have the default 
  // value.
  //
  ok = CheckGetCodePaths (p, "TestEnum", "TestA", EnumValue (AttributeObjectTest::TEST_A));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by default value");

  //
  // Set the Attribute using the EnumValue type.
  //
  ok = p->SetAttributeFailSafe("TestEnum", EnumValue (AttributeObjectTest::TEST_C));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() to TEST_C");

  ok = CheckGetCodePaths (p, "TestEnum", "TestC", EnumValue (AttributeObjectTest::TEST_C));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via EnumValue");

  //
  // Set the Attribute using the StringValue type.
  //
  ok = p->SetAttributeFailSafe("TestEnum", StringValue ("TestB"));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() to TEST_B");

  ok = CheckGetCodePaths (p, "TestEnum", "TestB", EnumValue (AttributeObjectTest::TEST_B));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Attribute not set properly by SetAttributeFailSafe() via StringValue");

  //
  // Try to set the Attribute to a bogus enum using the StringValue type and 
  // make sure the underlying value doesn't change.
  //
  ok = p->SetAttributeFailSafe("TestEnum", StringValue ("TestD"));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() to TEST_D"); // 

  ok = CheckGetCodePaths (p, "TestEnum", "TestB", EnumValue (AttributeObjectTest::TEST_B));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Error in SetAttributeFailSafe() but value changes");

  //
  // Try to set the Attribute to a bogus enum using an integer implicit conversion
  // and make sure the underlying value doesn't change.
  //
  ok = p->SetAttributeFailSafe("TestEnum", EnumValue (5));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() to 5");

  ok = CheckGetCodePaths (p, "TestEnum", "TestB", EnumValue (AttributeObjectTest::TEST_B));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Error in SetAttributeFailSafe() but value changes");

  return GetErrorStatus ();
}

template <> bool
AttributeTestCase<RandomVariableValue>::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // Try to set a UniformVariable
  //
  ok = p->SetAttributeFailSafe("TestRandom", RandomVariableValue (UniformVariable (0., 1.)));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() a UniformVariable");

  //
  // Try to set a <snicker> ConstantVariable
  //
  ok = p->SetAttributeFailSafe("TestRandom", RandomVariableValue (ConstantVariable (10.)));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() a UniformVariable");

  return GetErrorStatus ();
}

// ===========================================================================
// Test case for Object Vector Attributes.  Generic nature is pretty much lost
// here, so we just break the class out.
// ===========================================================================
class ObjectVectorAttributeTestCase : public TestCase
{
public:
  ObjectVectorAttributeTestCase (std::string description);
  virtual ~ObjectVectorAttributeTestCase () {}

private:
  virtual bool DoRun (void);
};

ObjectVectorAttributeTestCase::ObjectVectorAttributeTestCase (std::string description)
  : TestCase (description)
{
}

bool
ObjectVectorAttributeTestCase::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  ObjectVectorValue vector;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // When the object is first created, the Attribute should have no items in
  // the vector.
  //
  p->GetAttribute ("TestVector1", vector);
  NS_TEST_ASSERT_MSG_EQ (vector.GetN (), 0, "Initial count of ObjectVectorValue \"TestVector1\" should be zero");

  //
  // Adding to the attribute shouldn't affect the value we already have.
  //
  p->AddToVector1 ();
  NS_TEST_ASSERT_MSG_EQ (vector.GetN (), 0, "Initial count of ObjectVectorValue \"TestVector1\" should still be zero");

  //
  // Getting the attribute again should update the value.
  //
  p->GetAttribute ("TestVector1", vector);
  NS_TEST_ASSERT_MSG_EQ (vector.GetN (), 1, "ObjectVectorValue \"TestVector1\" should be incremented");

  //
  // Get the Object pointer from the value.
  //
  Ptr<Object> a = vector.Get (0);
  NS_TEST_ASSERT_MSG_NE (a, 0, "Ptr<Object> from VectorValue \"TestVector1\" is zero");

  //
  // Adding to the attribute shouldn't affect the value we already have.
  //
  p->AddToVector1 ();
  NS_TEST_ASSERT_MSG_EQ (vector.GetN (), 1, "Count of ObjectVectorValue \"TestVector1\" should still be one");

  //
  // Getting the attribute again should update the value.
  //
  p->GetAttribute ("TestVector1", vector);
  NS_TEST_ASSERT_MSG_EQ (vector.GetN (), 2, "ObjectVectorValue \"TestVector1\" should be incremented");

  return GetErrorStatus ();
}

// ===========================================================================
// Trace sources with value semantics can be used like Attributes.  Make sure
// we can use them that way.
// ===========================================================================
class IntegerTraceSourceAttributeTestCase : public TestCase
{
public:
  IntegerTraceSourceAttributeTestCase (std::string description);
  virtual ~IntegerTraceSourceAttributeTestCase () {}

private:
  virtual bool DoRun (void);
};

IntegerTraceSourceAttributeTestCase::IntegerTraceSourceAttributeTestCase (std::string description)
  : TestCase (description)
{
}

bool
IntegerTraceSourceAttributeTestCase::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  IntegerValue iv;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // When the object is first created, the Attribute should have the default 
  // value.
  //
  p->GetAttribute ("IntegerTraceSource1", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -2, "Attribute not set properly by default value");

  //
  // Set the Attribute to a positive value through an IntegerValue.
  //
  ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue (5));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to 5");

  p->GetAttribute ("IntegerTraceSource1", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 5, "Attribute not set properly by SetAttributeFailSafe() via IntegerValue");

  //
  // Limits should work.
  //
  ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue (127));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to 127");

  ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue (128));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via IntegerValue to 128");

  ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue (-128));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to -128");

  ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue (-129));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via IntegerValue to -129");

  //
  // When the object is first created, the Attribute should have the default 
  // value.
  //
  p->GetAttribute ("IntegerTraceSource2", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -2, "Attribute not set properly by default value");

  //
  // Set the Attribute to a positive value through an IntegerValue.
  //
  ok = p->SetAttributeFailSafe("IntegerTraceSource2", IntegerValue (5));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to 5");

  p->GetAttribute ("IntegerTraceSource2", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 5, "Attribute not set properly by SetAttributeFailSafe() via IntegerValue");

  //
  // Limits should work.
  //
  ok = p->SetAttributeFailSafe("IntegerTraceSource2", IntegerValue (127));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to 127");

  ok = p->SetAttributeFailSafe("IntegerTraceSource2", IntegerValue (128));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via IntegerValue to 128");

  ok = p->SetAttributeFailSafe("IntegerTraceSource2", IntegerValue (-128));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to -128");

  ok = p->SetAttributeFailSafe("IntegerTraceSource2", IntegerValue (-129));
  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() via IntegerValue to -129");

  return GetErrorStatus ();
}

// ===========================================================================
// Trace sources used like Attributes must also work as trace sources.  Make 
// sure we can use them that way.
// ===========================================================================
class IntegerTraceSourceTestCase : public TestCase
{
public:
  IntegerTraceSourceTestCase (std::string description);
  virtual ~IntegerTraceSourceTestCase () {}

private:
  virtual bool DoRun (void);

  void NotifySource1 (int8_t old, int8_t n) {m_got1 = n;}
  int64_t m_got1;
};

IntegerTraceSourceTestCase::IntegerTraceSourceTestCase (std::string description)
  : TestCase (description)
{
}

bool
IntegerTraceSourceTestCase::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // Check to make sure changing an Attibute value triggers a trace callback
  // that sets a member variable.
  //
  m_got1 = 1234;

  ok = p->SetAttributeFailSafe ("IntegerTraceSource1", IntegerValue (-1));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to -1");

  //
  // Source1 is declared as a TraceSourceAccessor to m_intSrc1.  This m_intSrc1
  // is also declared as an Integer Attribute.  We just checked to make sure we
  // could set it using an IntegerValue through its IntegerTraceSource1 "persona."
  // We should also be able to hook a trace source to the underlying variable.
  //
  ok = p->TraceConnectWithoutContext ("Source1", MakeCallback (&IntegerTraceSourceTestCase::NotifySource1, this));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not TraceConnectWithoutContext() \"Source1\" to NodifySource1()");

  //
  // When we set the IntegerValue that now underlies both the Integer Attribute
  // and the trace source, the trace should fire and call NotifySource1 which
  // will set m_got1 to the new value.
  //
  ok = p->SetAttributeFailSafe ("IntegerTraceSource1", IntegerValue (0));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to 0");

  NS_TEST_ASSERT_MSG_EQ (m_got1, 0, "Hitting a TracedValue does not cause trace callback to be called");

  //
  // Now disconnect from the trace source and ensure that the trace callback
  // is not called if the trace source is hit.
  //
  ok = p->TraceDisconnectWithoutContext ("Source1", MakeCallback (&IntegerTraceSourceTestCase::NotifySource1, this));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not TraceConnectWithoutContext() \"Source1\" to NodifySource1()");

  ok = p->SetAttributeFailSafe ("IntegerTraceSource1", IntegerValue (1));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() via IntegerValue to 1");

  NS_TEST_ASSERT_MSG_EQ (m_got1, 0, "Hitting a TracedValue after disconnect still causes callback");

  return GetErrorStatus ();
}

// ===========================================================================
// Trace sources used like Attributes must also work as trace sources.  Make 
// sure we can use them that way.
// ===========================================================================
class TracedCallbackTestCase : public TestCase
{
public:
  TracedCallbackTestCase (std::string description);
  virtual ~TracedCallbackTestCase () {}

private:
  virtual bool DoRun (void);

  void NotifySource2 (double a, int b, float c) {m_got2 = a;}

  double m_got2;
};

TracedCallbackTestCase::TracedCallbackTestCase (std::string description)
  : TestCase (description)
{
}

bool
TracedCallbackTestCase::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // Initialize the 
  //
  m_got2 = 4.3;

  //
  // Invoke the callback that lies at the heart of this test.  We have a
  // method InvokeCb() that just executes m_cb().  The variable m_cb is 
  // declared as a TracedCallback<double, int, float>.  This kind of beast
  // is like a callback but can call a list of targets.  This list should
  // be empty so nothing should happen now.  Specifically, m_got2 shouldn't
  // have changed.
  //
  p->InvokeCb (1.0, -5, 0.0);
  NS_TEST_ASSERT_MSG_EQ (m_got2, 4.3, "Invoking a newly created TracedCallback results in an unexpected callback");

  //
  // Now, wire the TracedCallback up to a trace sink.  This sink will just set
  // m_got2 to the first argument.
  //
  ok = p->TraceConnectWithoutContext ("Source2", MakeCallback (&TracedCallbackTestCase::NotifySource2, this));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not TraceConnectWithoutContext() to NotifySource2");

  //
  // Now if we invoke the callback, the trace source should fire and m_got2
  // should be set in the trace sink.
  //
  p->InvokeCb (1.0, -5, 0.0);
  NS_TEST_ASSERT_MSG_EQ (m_got2, 1.0, "Invoking TracedCallback does not result in trace callback");

  //
  // Now, disconnect the trace sink and see what happens when we invoke the
  // callback again.  Of course, the trace should not happen and m_got2 
  // should remain unchanged.
  //
  ok = p->TraceDisconnectWithoutContext ("Source2", MakeCallback (&TracedCallbackTestCase::NotifySource2, this));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not TraceDisconnectWithoutContext() from NotifySource2");

  p->InvokeCb (-1.0, -5, 0.0);
  NS_TEST_ASSERT_MSG_EQ (m_got2, 1.0, "Invoking disconnected TracedCallback unexpectedly results in trace callback");

  return GetErrorStatus ();
}

// ===========================================================================
// Smart pointers (Ptr) are central to our architecture, so they must work as
// attributes.
// ===========================================================================
class PointerAttributeTestCase : public TestCase
{
public:
  PointerAttributeTestCase (std::string description);
  virtual ~PointerAttributeTestCase () {}

private:
  virtual bool DoRun (void);

  void NotifySource2 (double a, int b, float c) {m_got2 = a;}

  double m_got2;
};

PointerAttributeTestCase::PointerAttributeTestCase (std::string description)
  : TestCase (description)
{
}

bool
PointerAttributeTestCase::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // We have declared a PointerValue Attribute named "Pointer" with a pointer
  // checker of type Derived.  This means that we should be able to pull out
  // a Ptr<Derived> with the initial value (which is 0).
  //
  PointerValue ptr;
  p->GetAttribute ("Pointer", ptr);
  Ptr<Derived> derived = ptr.Get<Derived> ();
  NS_TEST_ASSERT_MSG_EQ (derived, 0, "Unexpectedly found non-null pointer in newly initialized PointerValue Attribute");

  //
  // Now, lets create an Object of type Derived and set the local Ptr to point
  // to that object.  We can then set the PointerValue Attribute to that Ptr.
  //
  derived = Create<Derived> ();
  ok = p->SetAttributeFailSafe("Pointer", PointerValue (derived));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() a PointerValue of the correct type");

  //
  // Pull the value back out of the Attribute and make sure it points to the
  // correct object.
  //
  p->GetAttribute ("Pointer", ptr);
  Ptr<Derived> stored = ptr.Get<Derived> ();
  NS_TEST_ASSERT_MSG_EQ (stored, derived, "Retreived Attribute does not match stored PointerValue");

  //
  // We should be able to use the Attribute Get() just like GetObject<type>,
  // So see if we can get a Ptr<Object> out of the Ptr<Derived> we stored.
  // This should be a pointer to the same physical memory since its the 
  // same object.
  //
  p->GetAttribute ("Pointer", ptr);
  Ptr<Object> storedBase = ptr.Get<Object> ();
  NS_TEST_ASSERT_MSG_EQ (storedBase, stored, "Retreived Ptr<Object> does not match stored Ptr<Derived>");

  //
  // If we try to Get() something that is unrelated to what we stored, we should
  // retrieve a 0.
  //
  p->GetAttribute ("Pointer", ptr);
  Ptr<AttributeObjectTest> x = ptr.Get<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_EQ (x, 0, "Unexpectedly retreived unrelated Ptr<type> from stored Ptr<Derived>");

  //
  // We should be able to create the object From a list of attributes.
  //
  p = CreateObjectWithAttributes<AttributeObjectTest> ("Pointer", PointerValue (Create<Derived> ()));
  NS_TEST_ASSERT_MSG_NE (p, 0, "Could not create Object with PointerValue Attribute in Attribute List");

  derived = 0;
  p->GetAttribute ("Pointer", ptr);
  derived = ptr.Get<Derived> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Retrieved zero PointerValue Attribute after initializing to non-zero Ptr");

  return GetErrorStatus ();
}

// ===========================================================================
// Test the Attributes of type CallbackVale.
// ===========================================================================
class CallbackValueTestCase : public TestCase
{
public:
  CallbackValueTestCase (std::string description);
  virtual ~CallbackValueTestCase () {}

  void InvokeCbValue (int8_t a)
  {
    if (!m_cbValue.IsNull ()) {
        m_cbValue (a);
      }
  }

private:
  virtual bool DoRun (void);

  Callback<void,int8_t> m_cbValue;

  void NotifyCallbackValue (int8_t a) {m_gotCbValue = a;}

  int16_t m_gotCbValue;
};

CallbackValueTestCase::CallbackValueTestCase (std::string description)
  : TestCase (description)
{
}

bool
CallbackValueTestCase::DoRun (void)
{
  Ptr<AttributeObjectTest> p;
  bool ok;

  p = CreateObject<AttributeObjectTest> ();
  NS_TEST_ASSERT_MSG_NE (p, 0, "Unable to CreateObject");

  //
  // The member variable m_cbValue is declared as a Callback<void, int8_t>.  The
  // Attibute named "Callback" also points to m_cbValue and allows us to set the
  // callback using that Attribute.
  //
  // NotifyCallbackValue is going to be the target of the callback and will just set
  // m_gotCbValue to its single parameter.  This will be the parameter from the
  // callback invocation.  The method InvokeCbValue() just invokes the m_cbValue 
  // callback if it is non-null.
  //
  m_gotCbValue = 1;

  //
  // If we invoke the callback (which has not been set) nothing should happen.
  // Further, nothing should happen when we initialize the callback (it shouldn't
  // accidentally fire).
  //
  p->InvokeCbValue (2);
  CallbackValue cbValue = MakeCallback (&CallbackValueTestCase::NotifyCallbackValue, this);

  NS_TEST_ASSERT_MSG_EQ (m_gotCbValue, 1, "Callback unexpectedly fired");

  ok = p->SetAttributeFailSafe ("Callback", cbValue);
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() a CallbackValue");

  //
  // Now that the callback has been set, invoking it should set m_gotCbValue.
  //
  p->InvokeCbValue (2);
  NS_TEST_ASSERT_MSG_EQ (m_gotCbValue, 2, "Callback Attribute set by CallbackValue did not fire");

  ok = p->SetAttributeFailSafe ("Callback", CallbackValue (MakeNullCallback<void,int8_t> ()));
  NS_TEST_ASSERT_MSG_EQ (ok, true, "Could not SetAttributeFailSafe() a null CallbackValue");

  //
  // If the callback has been set to a null callback, it should no longer fire.
  //
  p->InvokeCbValue (3);
  NS_TEST_ASSERT_MSG_EQ (m_gotCbValue, 2, "Callback Attribute set to null callback unexpectedly fired");
  
  return GetErrorStatus ();
}

// ===========================================================================
// The Test Suite that glues all of the Test Cases together.
// ===========================================================================
class AttributesTestSuite : public TestSuite
{
public:
  AttributesTestSuite ();
};

AttributesTestSuite::AttributesTestSuite ()
  : TestSuite ("attributes", UNIT)
{
  AddTestCase (new AttributeTestCase<BooleanValue> ("Check Attributes of type BooleanValue"));
  AddTestCase (new AttributeTestCase<IntegerValue> ("Check Attributes of type IntegerValue"));
  AddTestCase (new AttributeTestCase<UintegerValue> ("Check Attributes of type UintegerValue"));
  AddTestCase (new AttributeTestCase<DoubleValue> ("Check Attributes of type DoubleValue"));
  AddTestCase (new AttributeTestCase<EnumValue> ("Check Attributes of type EnumValue"));
  AddTestCase (new AttributeTestCase<RandomVariableValue> ("Check Attributes of type RandomVariableValue"));
  AddTestCase (new ObjectVectorAttributeTestCase ("Check Attributes of type ObjectVectorValue"));
  AddTestCase (new IntegerTraceSourceAttributeTestCase ("Ensure TracedValue<uint8_t> can be set like IntegerValue"));
  AddTestCase (new IntegerTraceSourceTestCase ("Ensure TracedValue<uint8_t> also works as trace source"));
  AddTestCase (new TracedCallbackTestCase ("Ensure TracedCallback<double, int, float> works as trace source"));
  AddTestCase (new PointerAttributeTestCase ("Check Attributes of type PointerValue"));
  AddTestCase (new CallbackValueTestCase ("Check Attributes of type CallbackValue"));
}

AttributesTestSuite attributesTestSuite;

} // namespace ns3
