From 6f53796ccf4dc29368920a322baeaceb3fd2266f Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Mon, 27 Feb 2012 14:47:40 -0500
Subject: Add LLInstanceTracker test for exception in subclass constructor. We
 want to verify the sequence: LLInstanceTracker constructor adds instance to
 underlying container Subclass constructor throws exception LLInstanceTracker
 destructor removes instance from underlying container.

---
 indra/llcommon/tests/llinstancetracker_test.cpp | 62 +++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

(limited to 'indra')

diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp
index b34d1c5fd3..fda932c355 100644
--- a/indra/llcommon/tests/llinstancetracker_test.cpp
+++ b/indra/llcommon/tests/llinstancetracker_test.cpp
@@ -35,6 +35,7 @@
 #include <vector>
 #include <set>
 #include <algorithm>                // std::sort()
+#include <stdexcept>
 // std headers
 // external library headers
 #include <boost/scoped_ptr.hpp>
@@ -42,6 +43,11 @@
 #include "../test/lltut.h"
 #include "wrapllerrs.h"
 
+struct Badness: public std::runtime_error
+{
+    Badness(const std::string& what): std::runtime_error(what) {}
+};
+
 struct Keyed: public LLInstanceTracker<Keyed, std::string>
 {
     Keyed(const std::string& name):
@@ -53,6 +59,17 @@ struct Keyed: public LLInstanceTracker<Keyed, std::string>
 
 struct Unkeyed: public LLInstanceTracker<Unkeyed>
 {
+    Unkeyed(const std::string& thrw="")
+    {
+        // LLInstanceTracker should respond appropriately if a subclass
+        // constructor throws an exception. Specifically, it should run
+        // LLInstanceTracker's destructor and remove itself from the
+        // underlying container.
+        if (! thrw.empty())
+        {
+            throw Badness(thrw);
+        }
+    }
 };
 
 /*****************************************************************************
@@ -229,4 +246,49 @@ namespace tut
         }
         ensure(! what.empty());
     }
+
+    template<> template<>
+    void object::test<8>()
+    {
+        set_test_name("exception in subclass ctor");
+        typedef std::set<Unkeyed*> InstanceSet;
+        InstanceSet existing;
+        // We can't use the iterator-range InstanceSet constructor because
+        // beginInstances() returns an iterator that dereferences to an
+        // Unkeyed&, not an Unkeyed*.
+        for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
+                                    ukend(Unkeyed::endInstances());
+             uki != ukend; ++uki)
+        {
+            existing.insert(&*uki);
+        }
+        Unkeyed* puk = NULL;
+        try
+        {
+            // We don't expect the assignment to take place because we expect
+            // Unkeyed to respond to the non-empty string param by throwing.
+            // We know the LLInstanceTracker base-class constructor will have
+            // run before Unkeyed's constructor, therefore the new instance
+            // will have added itself to the underlying set. The whole
+            // question is, when Unkeyed's constructor throws, will
+            // LLInstanceTracker's destructor remove it from the set? I
+            // realize we're testing the C++ implementation more than
+            // Unkeyed's implementation, but this seems an important point to
+            // nail down.
+            puk = new Unkeyed("throw");
+        }
+        catch (const Badness&)
+        {
+        }
+        // Ensure that every member of the new, updated set of Unkeyed
+        // instances was also present in the original set. If that's not true,
+        // it's because our new Unkeyed ended up in the updated set despite
+        // its constructor exception.
+        for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
+                                    ukend(Unkeyed::endInstances());
+             uki != ukend; ++uki)
+        {
+            ensure("failed to remove instance", existing.find(&*uki) != existing.end());
+        }
+    }
 } // namespace tut
-- 
cgit v1.2.3