src/core/test/config-test-suite.cc
author Tom Henderson <tomh@tomh.org>
Fri, 12 Sep 2014 18:19:47 -0700
changeset 10942 2011df8ec4f6
parent 10939 804e8b5a81bd
child 10978 754c8256c35c
permissions -rw-r--r--
silence compiler warning

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2008 INRIA
 *
 * 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
 *
 * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
 */
#include "ns3/config.h"
#include "ns3/test.h"
#include "ns3/integer.h"
#include "ns3/traced-value.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/callback.h"

#include "ns3/singleton.h"
#include "ns3/object.h"
#include "ns3/object-vector.h"
#include "ns3/names.h"
#include "ns3/pointer.h"
#include "ns3/log.h"


#include <sstream>

using namespace ns3;

// ===========================================================================
// An object with some attributes that we can play with using config.
// ===========================================================================
class ConfigTestObject : public Object
{
public:
  static TypeId GetTypeId (void);

  void AddNodeA (Ptr<ConfigTestObject> a);
  void AddNodeB (Ptr<ConfigTestObject> b);

  void SetNodeA (Ptr<ConfigTestObject> a);
  void SetNodeB (Ptr<ConfigTestObject> b);

  int8_t GetA (void) const;
  int8_t GetB (void) const;

private:
  std::vector<Ptr<ConfigTestObject> > m_nodesA;
  std::vector<Ptr<ConfigTestObject> > m_nodesB;
  Ptr<ConfigTestObject> m_nodeA;
  Ptr<ConfigTestObject> m_nodeB;
  int8_t m_a;
  int8_t m_b;
  TracedValue<int16_t> m_trace;
};

TypeId
ConfigTestObject::GetTypeId (void)
{
  static TypeId tid = TypeId ("ConfigTestObject")
    .SetParent<Object> ()
    .AddAttribute ("NodesA", "",
                   ObjectVectorValue (),
                   MakeObjectVectorAccessor (&ConfigTestObject::m_nodesA),
                   MakeObjectVectorChecker<ConfigTestObject> ())
    .AddAttribute ("NodesB", "",
                   ObjectVectorValue (),
                   MakeObjectVectorAccessor (&ConfigTestObject::m_nodesB),
                   MakeObjectVectorChecker<ConfigTestObject> ())
    .AddAttribute ("NodeA", "",
                   PointerValue (),
                   MakePointerAccessor (&ConfigTestObject::m_nodeA),
                   MakePointerChecker<ConfigTestObject> ())
    .AddAttribute ("NodeB", "",
                   PointerValue (),
                   MakePointerAccessor (&ConfigTestObject::m_nodeB),
                   MakePointerChecker<ConfigTestObject> ())
    .AddAttribute ("A", "",
                   IntegerValue (10),
                   MakeIntegerAccessor (&ConfigTestObject::m_a),
                   MakeIntegerChecker<int8_t> ())
    .AddAttribute ("B", "",
                   IntegerValue (9),
                   MakeIntegerAccessor (&ConfigTestObject::m_b),
                   MakeIntegerChecker<int8_t> ())
    .AddAttribute ("Source", "XX",
                   IntegerValue (-1),
                   MakeIntegerAccessor (&ConfigTestObject::m_trace),
                   MakeIntegerChecker<int16_t> ())
    .AddTraceSource ("Source", "XX",
                     MakeTraceSourceAccessor (&ConfigTestObject::m_trace))
  ;
  return tid;
}

void
ConfigTestObject::SetNodeA (Ptr<ConfigTestObject> a)
{
  m_nodeA = a;
}

void
ConfigTestObject::SetNodeB (Ptr<ConfigTestObject> b)
{
  m_nodeB = b;
}

void 
ConfigTestObject::AddNodeA (Ptr<ConfigTestObject> a)
{
  m_nodesA.push_back (a);
}

void 
ConfigTestObject::AddNodeB (Ptr<ConfigTestObject> b)
{
  m_nodesB.push_back (b);
}

int8_t 
ConfigTestObject::GetA (void) const
{
  return m_a;
}

int8_t 
ConfigTestObject::GetB (void) const
{
  return m_b;
}

// Other test objects
//

class DerivedConfigTestObject : public ConfigTestObject
{
public:
  static TypeId GetTypeId (void);
  DerivedConfigTestObject (void) {}
  virtual ~DerivedConfigTestObject (void) {}
};

TypeId
DerivedConfigTestObject::GetTypeId (void)
{
  static TypeId tid = TypeId ("DerivedConfigTestObject")
    .SetParent<ConfigTestObject> ()
    ;
  return tid;
}

class BaseConfigObject : public Object
{
public:
  static TypeId GetTypeId (void);
  BaseConfigObject (void) : m_x(15) {}
  virtual ~BaseConfigObject (void) {}
private:
  int8_t m_x;
  void Increment (void) { m_x++; } // silence unused variable warning
};

TypeId
BaseConfigObject::GetTypeId (void)
{
  static TypeId tid = TypeId ("BaseConfigObject")
    .SetParent<Object> ()
    .AddAttribute ("X", "",
                   IntegerValue (10),
                   MakeIntegerAccessor (&BaseConfigObject::m_x),
                   MakeIntegerChecker<int8_t> ())
    ;
  return tid;
}

class DerivedConfigObject : public BaseConfigObject
{
public:
  static TypeId GetTypeId (void);
  DerivedConfigObject (void) {}
  virtual ~DerivedConfigObject (void) {}
};

TypeId
DerivedConfigObject::GetTypeId (void)
{
  static TypeId tid = TypeId ("DerivedConfigObject")
    .SetParent<BaseConfigObject> ()
    ;
  return tid;
}


// ===========================================================================
// Test for the ability to register and use a root namespace
// ===========================================================================
class RootNamespaceConfigTestCase : public TestCase
{
public:
  RootNamespaceConfigTestCase ();
  virtual ~RootNamespaceConfigTestCase () {}

private:
  virtual void DoRun (void);
};

RootNamespaceConfigTestCase::RootNamespaceConfigTestCase ()
  : TestCase ("Check ability to register a root namespace and use it")
{
}

void
RootNamespaceConfigTestCase::DoRun (void)
{
  IntegerValue iv;
  //
  // Create an object and register its attributes directly in the root 
  // namespace.
  //
  Ptr<ConfigTestObject> root = CreateObject<ConfigTestObject> ();
  Config::RegisterRootNamespaceObject (root);

  //
  // We should find the default values there.
  //
  root->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" not initialized as expected");

  //
  // Now use the config mechanism to set the attribute; and we should find the
  // new value.
  //
  Config::Set ("/A", IntegerValue (1));
  root->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 1, "Object Attribute \"A\" not set correctly");

  //
  // We should find the default values of "B" too.
  //
  root->GetAttribute ("B", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 9, "Object Attribute \"B\" not initialized as expected");

  //
  // Now use the config mechanism to set the attribute; and we should find the
  // new value.
  //
  Config::Set ("/B", IntegerValue (-1));
  root->GetAttribute ("B", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -1, "Object Attribute \"B\" not set correctly");
}

// ===========================================================================
// Test for the ability to add an object under the root namespace.
// ===========================================================================
class UnderRootNamespaceConfigTestCase : public TestCase
{
public:
  UnderRootNamespaceConfigTestCase ();
  virtual ~UnderRootNamespaceConfigTestCase () {}

private:
  virtual void DoRun (void);
};

UnderRootNamespaceConfigTestCase::UnderRootNamespaceConfigTestCase ()
  : TestCase ("Check ability to register an object under the root namespace and use it")
{
}

void
UnderRootNamespaceConfigTestCase::DoRun (void)
{
  IntegerValue iv;
  //
  // Create an object and register its attributes directly in the root 
  // namespace.
  //
  Ptr<ConfigTestObject> root = CreateObject<ConfigTestObject> ();
  Config::RegisterRootNamespaceObject (root);

  Ptr<ConfigTestObject> a = CreateObject<ConfigTestObject> ();
  root->SetNodeA (a);

  //
  // We should find the default values there.
  //
  a->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" not initialized as expected");

  //
  // Now use the config mechanism to set the attribute; and we should find the
  // new value.
  //
  Config::Set ("/NodeA/A", IntegerValue (1));
  a->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 1, "Object Attribute \"A\" not set correctly");


  //
  // We should find the default values of "B" too.
  //
  a->GetAttribute ("B", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 9, "Object Attribute \"B\" not initialized as expected");

  //
  // Now use the config mechanism to set the attribute; and we should find the
  // new value.
  //
  Config::Set ("/NodeA/B", IntegerValue (-1));
  a->GetAttribute ("B", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -1, "Object Attribute \"B\" not set correctly");

  //
  // Try and set through a nonexistent path.  Should do nothing.
  //
  Config::Set ("/NodeB/A", IntegerValue (1234));
  a->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 1, "Object Attribute \"A\" unexpectedly set via bad path");

  Config::Set ("/NodeB/B", IntegerValue (1234));
  a->GetAttribute ("B", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -1, "Object Attribute \"B\" unexpectedly set via bad path");

  //
  // Step down one level of recursion and try again
  //
  Ptr<ConfigTestObject> b = CreateObject<ConfigTestObject> ();

  //
  // We should find the default values there.
  //
  b->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" not initialized as expected");
  b->GetAttribute ("B", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 9, "Object Attribute \"B\" not initialized as expected");

  //
  // Now tell A that it has a B; and we should be able to set this new object's
  // Attributes.
  //
  a->SetNodeB (b);

  Config::Set ("/NodeA/NodeB/A", IntegerValue (4));
  Config::Set ("/NodeA/NodeB/B", IntegerValue (-4));
  b->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 4, "Object Attribute \"A\" not set as expected");
  b->GetAttribute ("B", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -4, "Object Attribute \"B\" not set as expected");


  //
  // Try '*' for attributes
  //
  Config::Set ("/*/A", IntegerValue (2));
  a->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 2, "Object Attribute \"A\" not set correctly");
  b->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 4, "Object Attribute \"A\" not set correctly");
}

// ===========================================================================
// Test for the ability to deal configure with vectors of objects.
// ===========================================================================
class ObjectVectorConfigTestCase : public TestCase
{
public:
  ObjectVectorConfigTestCase ();
  virtual ~ObjectVectorConfigTestCase () {}

private:
  virtual void DoRun (void);
};

ObjectVectorConfigTestCase::ObjectVectorConfigTestCase ()
  : TestCase ("Check ability to configure vectors of Object using regular expressions")
{
}

void
ObjectVectorConfigTestCase::DoRun (void)
{
  IntegerValue iv;

  //
  // Create a root namespace object
  //
  Ptr<ConfigTestObject> root = CreateObject<ConfigTestObject> ();
  Config::RegisterRootNamespaceObject (root);

  //
  // Create an object under the root.
  //
  Ptr<ConfigTestObject> a = CreateObject<ConfigTestObject> ();
  root->SetNodeA (a);

  //
  // Create an object one level down.
  //
  Ptr<ConfigTestObject> b = CreateObject<ConfigTestObject> ();
  a->SetNodeB (b);

  //
  // Add four objects to the ObjectVector Attribute at the bottom of the 
  // object hierarchy.  By this point, we believe that the Attributes
  // will be initialized correctly.
  //
  Ptr<ConfigTestObject> obj0 = CreateObject<ConfigTestObject> ();
  Ptr<ConfigTestObject> obj1 = CreateObject<ConfigTestObject> ();
  Ptr<ConfigTestObject> obj2 = CreateObject<ConfigTestObject> ();
  Ptr<ConfigTestObject> obj3 = CreateObject<ConfigTestObject> ();
  b->AddNodeB (obj0);
  b->AddNodeB (obj1);
  b->AddNodeB (obj2);
  b->AddNodeB (obj3);

  //
  // Set an Attribute of the zeroth Object in the vector by explicitly writing
  // the '0' and make sure that only the one thing changed.
  //
  Config::Set ("/NodeA/NodeB/NodesB/0/A", IntegerValue (-11));
  obj0->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -11, "Object Attribute \"A\" not set as expected");

  obj1->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" unexpectedly set");

  obj2->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" unexpectedly set");

  obj3->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" unexpectedly set");

  //
  // Start using regular expression-like syntax to set Attributes.  First try
  // the OR syntax.  Make sure that the two objects changed and nothing else
  //
  Config::Set ("/NodeA/NodeB/NodesB/0|1/A", IntegerValue (-12));
  obj0->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -12, "Object Attribute \"A\" not set as expected");

  obj1->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -12, "Object Attribute \"A\" not set as expected");

  obj2->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" unexpectedly set");

  obj3->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" unexpectedly set");

  //
  // Make sure that extra '|' are allowed at the start and end of the regular expression
  //
  Config::Set ("/NodeA/NodeB/NodesB/|0|1|/A", IntegerValue (-13));
  obj0->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -13, "Object Attribute \"A\" not set as expected");

  obj1->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -13, "Object Attribute \"A\" not set as expected");

  obj2->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" unexpectedly set");

  obj3->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" unexpectedly set");

  //
  // Try the [x-y] syntax
  //
  Config::Set ("/NodeA/NodeB/NodesB/[0-2]/A", IntegerValue (-14));
  obj0->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -14, "Object Attribute \"A\" not set as expected");

  obj1->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -14, "Object Attribute \"A\" not set as expected");

  obj2->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -14, "Object Attribute \"A\" not set as expected");

  obj3->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 10, "Object Attribute \"A\" unexpectedly set");

  //
  // Try the [x-y] syntax at the other limit
  //
  Config::Set ("/NodeA/NodeB/NodesB/[1-3]/A", IntegerValue (-15));
  obj0->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -14, "Object Attribute \"A\" unexpectedly set");

  obj1->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -15, "Object Attribute \"A\" not set as expected");

  obj2->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -15, "Object Attribute \"A\" not set as expected");

  obj3->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -15, "Object Attribute \"A\" not set as expected");

  //
  // Combine the [x-y] syntax and the OR sntax
  //
  Config::Set ("/NodeA/NodeB/NodesB/[0-1]|3/A", IntegerValue (-16));
  obj0->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -16, "Object Attribute \"A\" not set as expected");

  obj1->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -16, "Object Attribute \"A\" not set as expected");

  obj2->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -15, "Object Attribute \"A\" unexpectedly set");

  obj3->GetAttribute ("A", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), -16, "Object Attribute \"A\" not set as expected");
}

// ===========================================================================
// Test for the ability to trace configure with vectors of objects.
// ===========================================================================
class ObjectVectorTraceConfigTestCase : public TestCase
{
public:
  ObjectVectorTraceConfigTestCase ();
  virtual ~ObjectVectorTraceConfigTestCase () {}

  void Trace (int16_t oldValue, int16_t newValue) { m_newValue = newValue; }
  void TraceWithPath (std::string path, int16_t old, int16_t newValue) { m_newValue = newValue; m_path = path; }

private:
  virtual void DoRun (void);

  int16_t m_newValue;
  std::string m_path;
};

ObjectVectorTraceConfigTestCase::ObjectVectorTraceConfigTestCase ()
  : TestCase ("Check ability to trace connect through vectors of Object using regular expressions")
{
}

void
ObjectVectorTraceConfigTestCase::DoRun (void)
{
  IntegerValue iv;

  //
  // Create a root namespace object
  //
  Ptr<ConfigTestObject> root = CreateObject<ConfigTestObject> ();
  Config::RegisterRootNamespaceObject (root);

  //
  // Create an object under the root.
  //
  Ptr<ConfigTestObject> a = CreateObject<ConfigTestObject> ();
  root->SetNodeA (a);

  //
  // Create an object one level down.
  //
  Ptr<ConfigTestObject> b = CreateObject<ConfigTestObject> ();
  a->SetNodeB (b);

  //
  // Add four objects to the ObjectVector Attribute at the bottom of the 
  // object hierarchy.  By this point, we believe that the Attributes
  // will be initialized correctly.
  //
  Ptr<ConfigTestObject> obj0 = CreateObject<ConfigTestObject> ();
  Ptr<ConfigTestObject> obj1 = CreateObject<ConfigTestObject> ();
  Ptr<ConfigTestObject> obj2 = CreateObject<ConfigTestObject> ();
  Ptr<ConfigTestObject> obj3 = CreateObject<ConfigTestObject> ();
  b->AddNodeB (obj0);
  b->AddNodeB (obj1);
  b->AddNodeB (obj2);
  b->AddNodeB (obj3);

  //
  // Do a trace connect to some of the sources.  We already checked parsing of
  // the regular expressions, so we'll concentrate on the tracing part of the 
  // puzzle here.
  //
  Config::ConnectWithoutContext ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
                                 MakeCallback (&ObjectVectorTraceConfigTestCase::Trace, this));

  // 
  // If we bug the trace source referred to by index '0' above, we should see 
  // the trace fire.
  //
  m_newValue = 0;
  obj0->SetAttribute ("Source", IntegerValue (-1));
  NS_TEST_ASSERT_MSG_EQ (m_newValue, -1, "Trace 0 did not fire as expected");

  // 
  // If we bug the trace source referred to by index '1' above, we should see 
  // the trace fire.
  //
  m_newValue = 0;
  obj1->SetAttribute ("Source", IntegerValue (-2));
  NS_TEST_ASSERT_MSG_EQ (m_newValue, -2, "Trace 1 did not fire as expected");

  // 
  // If we bug the trace source referred to by index '2' which is skipped above,
  // we should not see the trace fire.
  //
  m_newValue = 0;
  obj2->SetAttribute ("Source", IntegerValue (-3));
  NS_TEST_ASSERT_MSG_EQ (m_newValue, 0, "Trace 2 fired unexpectedly");

  // 
  // If we bug the trace source referred to by index '3' above, we should see 
  // the trace fire.
  //
  m_newValue = 0;
  obj3->SetAttribute ("Source", IntegerValue (-4));
  NS_TEST_ASSERT_MSG_EQ (m_newValue, -4, "Trace 3 did not fire as expected");

  //
  // Do a trace connect (with context) to some of the sources.
  //
  Config::Connect ("/NodeA/NodeB/NodesB/[0-1]|3/Source", 
                   MakeCallback (&ObjectVectorTraceConfigTestCase::TraceWithPath, this));

  // 
  // If we bug the trace source referred to by index '0' above, we should see 
  // the trace fire with the expected context path.
  //
  m_newValue = 0;
  m_path = "";
  obj0->SetAttribute ("Source", IntegerValue (-1));
  NS_TEST_ASSERT_MSG_EQ (m_newValue, -1, "Trace 0 did not fire as expected");
  NS_TEST_ASSERT_MSG_EQ (m_path, "/NodeA/NodeB/NodesB/0/Source", "Trace 0 did not provide expected context");

  // 
  // If we bug the trace source referred to by index '1' above, we should see 
  // the trace fire with the expected context path.
  //
  m_newValue = 0;
  m_path = "";
  obj1->SetAttribute ("Source", IntegerValue (-2));
  NS_TEST_ASSERT_MSG_EQ (m_newValue, -2, "Trace 1 did not fire as expected");
  NS_TEST_ASSERT_MSG_EQ (m_path, "/NodeA/NodeB/NodesB/1/Source", "Trace 1 did not provide expected context");

  // 
  // If we bug the trace source referred to by index '2' which is skipped above,
  // we should not see the trace fire.
  //
  m_newValue = 0;
  m_path = "";
  obj2->SetAttribute ("Source", IntegerValue (-3));
  NS_TEST_ASSERT_MSG_EQ (m_newValue, 0, "Trace 2 fired unexpectedly");

  // 
  // If we bug the trace source referred to by index '3' above, we should see 
  // the trace fire with the expected context path.
  //
  m_newValue = 0;
  m_path = "";
  obj3->SetAttribute ("Source", IntegerValue (-4));
  NS_TEST_ASSERT_MSG_EQ (m_newValue, -4, "Trace 3 did not fire as expected");
  NS_TEST_ASSERT_MSG_EQ (m_path, "/NodeA/NodeB/NodesB/1/Source", "Trace 1 did not provide expected context");
}

// ===========================================================================
// Test for the ability to search attributes of parent classes
// when Resolver searches for attributes in a derived class object.
// This test passes with the patch found in
// https://www.nsnam.org/bugzilla/show_bug.cgi?id=1673
// (also reported in https://www.nsnam.org/bugzilla/show_bug.cgi?id=1959)
// ===========================================================================
class SearchAttributesOfParentObjectsTestCase : public TestCase
{
public:
  SearchAttributesOfParentObjectsTestCase ();
  virtual ~SearchAttributesOfParentObjectsTestCase () {}

private:
  virtual void DoRun (void);

};

SearchAttributesOfParentObjectsTestCase::SearchAttributesOfParentObjectsTestCase ()
  : TestCase ("Check that attributes of base class are searchable from paths including objects of derived class")
{
}

void
SearchAttributesOfParentObjectsTestCase::DoRun (void)
{
  IntegerValue iv;
  //
  // Create a root namespace object that doesn't have attributes but
  // whose parent class has 'NodeA' attribute
  //
  Ptr<DerivedConfigTestObject> root = CreateObject<DerivedConfigTestObject> ();
  Config::RegisterRootNamespaceObject (root);

  //
  //  Instantiate /NodeA
  //
  Ptr<DerivedConfigTestObject> a = CreateObject<DerivedConfigTestObject> ();
  root->SetNodeA (a);

  //
  // BaseConfigObject has attribute X, but we aggregate DerivedConfigObject 
  // instead
  //
  Ptr<DerivedConfigObject> derived = CreateObject<DerivedConfigObject> ();
  a->AggregateObject (derived);
  Config::Set ("/NodeA/$DerivedConfigObject/X", IntegerValue (42));
  derived->GetAttribute ("X", iv);
  NS_TEST_ASSERT_MSG_EQ (iv.Get (), 42, "Object Attribute \"X\" not settable in derived class");

}

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

ConfigTestSuite::ConfigTestSuite ()
  : TestSuite ("config", UNIT)
{
  AddTestCase (new RootNamespaceConfigTestCase, TestCase::QUICK);
  AddTestCase (new UnderRootNamespaceConfigTestCase, TestCase::QUICK);
  AddTestCase (new ObjectVectorConfigTestCase, TestCase::QUICK);
  AddTestCase (new SearchAttributesOfParentObjectsTestCase, TestCase::QUICK);
}

static ConfigTestSuite configTestSuite;