Allow ObjDefManager instances to be cloned
authorsfan5 <sfan5@live.de>
Thu, 9 Apr 2020 21:40:12 +0000 (23:40 +0200)
committerLoïc Blot <nerzhul@users.noreply.github.com>
Tue, 5 May 2020 17:26:59 +0000 (19:26 +0200)
13 files changed:
src/mapgen/mg_biome.cpp
src/mapgen/mg_biome.h
src/mapgen/mg_decoration.cpp
src/mapgen/mg_decoration.h
src/mapgen/mg_ore.cpp
src/mapgen/mg_ore.h
src/mapgen/mg_schematic.cpp
src/mapgen/mg_schematic.h
src/nodedef.cpp
src/nodedef.h
src/objdef.cpp
src/objdef.h
src/unittest/test_objdef.cpp

index 345bc8c6a1606e50edc50cc24cec4f9560e66890..3a72c43bfcb433a4f251862579314ef7cab810d7 100644 (file)
@@ -92,6 +92,16 @@ void BiomeManager::clear()
 }
 
 
+BiomeManager *BiomeManager::clone() const
+{
+       auto mgr = new BiomeManager();
+       assert(mgr);
+       ObjDefManager::cloneTo(mgr);
+       mgr->m_server = m_server;
+       return mgr;
+}
+
+
 // For BiomeGen type 'BiomeGenOriginal'
 float BiomeManager::getHeatAtPosOriginal(v3s16 pos, NoiseParams &np_heat,
        NoiseParams &np_heat_blend, u64 seed)
@@ -321,6 +331,41 @@ Biome *BiomeGenOriginal::calcBiomeFromNoise(float heat, float humidity, v3s16 po
 
 ////////////////////////////////////////////////////////////////////////////////
 
+ObjDef *Biome::clone() const
+{
+       auto obj = new Biome();
+       ObjDef::cloneTo(obj);
+       NodeResolver::cloneTo(obj);
+
+       obj->flags = flags;
+
+       obj->c_top = c_top;
+       obj->c_filler = c_filler;
+       obj->c_stone = c_stone;
+       obj->c_water_top = c_water_top;
+       obj->c_water = c_water;
+       obj->c_river_water = c_river_water;
+       obj->c_riverbed = c_riverbed;
+       obj->c_dust = c_dust;
+       obj->c_cave_liquid = c_cave_liquid;
+       obj->c_dungeon = c_dungeon;
+       obj->c_dungeon_alt = c_dungeon_alt;
+       obj->c_dungeon_stair = c_dungeon_stair;
+
+       obj->depth_top = depth_top;
+       obj->depth_filler = depth_filler;
+       obj->depth_water_top = depth_water_top;
+       obj->depth_riverbed = depth_riverbed;
+
+       obj->min_pos = min_pos;
+       obj->max_pos = max_pos;
+       obj->heat_point = heat_point;
+       obj->humidity_point = humidity_point;
+       obj->vertical_blend = vertical_blend;
+
+       return obj;
+}
+
 void Biome::resolveNodeNames()
 {
        getIdFromNrBacklog(&c_top,           "mapgen_stone",              CONTENT_AIR,    false);
index ee148adbc783720d076fe9e096ac0697487fd489..6aadc32fac46fa75cb3bc30860ae65807824ba00 100644 (file)
@@ -42,6 +42,8 @@ enum BiomeType {
 
 class Biome : public ObjDef, public NodeResolver {
 public:
+       ObjDef *clone() const;
+
        u32 flags;
 
        content_t c_top;
@@ -191,6 +193,8 @@ public:
        BiomeManager(Server *server);
        virtual ~BiomeManager() = default;
 
+       BiomeManager *clone() const;
+
        const char *getObjectTitle() const
        {
                return "biome";
@@ -232,6 +236,8 @@ public:
        Biome *getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos);
 
 private:
+       BiomeManager() {};
+
        Server *m_server;
 
 };
index 28dde0209250421e2a1b8a9bbfcce728facf7972..db9c696ed714af01a1ef32aa362e48ca1ff7712c 100644 (file)
@@ -67,6 +67,13 @@ size_t DecorationManager::placeAllDecos(Mapgen *mg, u32 blockseed,
        return nplaced;
 }
 
+DecorationManager *DecorationManager::clone() const
+{
+       auto mgr = new DecorationManager();
+       ObjDefManager::cloneTo(mgr);
+       return mgr;
+}
+
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -269,9 +276,42 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
 }
 
 
+void Decoration::cloneTo(Decoration *def) const
+{
+       ObjDef::cloneTo(def);
+       def->flags = flags;
+       def->mapseed = mapseed;
+       def->c_place_on = c_place_on;
+       def->sidelen = sidelen;
+       def->y_min = y_min;
+       def->y_max = y_max;
+       def->fill_ratio = fill_ratio;
+       def->np = np;
+       def->c_spawnby = c_spawnby;
+       def->nspawnby = nspawnby;
+       def->place_offset_y = place_offset_y;
+       def->biomes = biomes;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *DecoSimple::clone() const
+{
+       auto def = new DecoSimple();
+       Decoration::cloneTo(def);
+
+       def->c_decos = c_decos;
+       def->deco_height = deco_height;
+       def->deco_height_max = deco_height_max;
+       def->deco_param2 = deco_param2;
+       def->deco_param2_max = deco_param2_max;
+
+       return def;
+}
+
+
 void DecoSimple::resolveNodeNames()
 {
        Decoration::resolveNodeNames();
@@ -351,6 +391,21 @@ size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *DecoSchematic::clone() const
+{
+       auto def = new DecoSchematic();
+       Decoration::cloneTo(def);
+       NodeResolver::cloneTo(def);
+
+       def->rotation = rotation;
+       /* FIXME: This is not ideal, we only have a pointer to the schematic despite
+        * not owning it. Optimally this would be a handle. */
+       def->schematic = schematic; // not cloned
+
+       return def;
+}
+
+
 size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
 {
        // Schematic could have been unloaded but not the decoration
index 03fec04fd19877f37728e3391ac6342fdf46363a..af8fcd7bf915c78139629e2a7e8f6600f69fd9e8 100644 (file)
@@ -73,11 +73,16 @@ public:
        s16 place_offset_y = 0;
 
        std::unordered_set<u8> biomes;
+
+protected:
+       void cloneTo(Decoration *def) const;
 };
 
 
 class DecoSimple : public Decoration {
 public:
+       ObjDef *clone() const;
+
        virtual void resolveNodeNames();
        virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
 
@@ -91,6 +96,8 @@ public:
 
 class DecoSchematic : public Decoration {
 public:
+       ObjDef *clone() const;
+
        DecoSchematic() = default;
 
        virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
@@ -113,6 +120,8 @@ public:
        DecorationManager(IGameDef *gamedef);
        virtual ~DecorationManager() = default;
 
+       DecorationManager *clone() const;
+
        const char *getObjectTitle() const
        {
                return "decoration";
@@ -133,4 +142,7 @@ public:
        }
 
        size_t placeAllDecos(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+
+private:
+       DecorationManager() {};
 };
index c36249cb9e1fa0ffe55dfaf6e66dfafefa8b316a..db647f82be0199e16785c7819a263d24e2b5e3f2 100644 (file)
@@ -72,6 +72,14 @@ void OreManager::clear()
 }
 
 
+OreManager *OreManager::clone() const
+{
+       auto mgr = new OreManager();
+       ObjDefManager::cloneTo(mgr);
+       return mgr;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 
@@ -106,9 +114,37 @@ size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
 }
 
 
+void Ore::cloneTo(Ore *def) const
+{
+       ObjDef::cloneTo(def);
+       NodeResolver::cloneTo(def);
+       def->c_ore = c_ore;
+       def->c_wherein = c_wherein;
+       def->clust_scarcity = clust_scarcity;
+       def->clust_num_ores = clust_num_ores;
+       def->clust_size = clust_size;
+       def->y_min = y_min;
+       def->y_max = y_max;
+       def->ore_param2 = ore_param2;
+       def->flags = flags;
+       def->nthresh = nthresh;
+       def->np = np;
+       def->noise = nullptr; // cannot be shared! so created on demand
+       def->biomes = biomes;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *OreScatter::clone() const
+{
+       auto def = new OreScatter();
+       Ore::cloneTo(def);
+       return def;
+}
+
+
 void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
        v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -158,6 +194,19 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *OreSheet::clone() const
+{
+       auto def = new OreSheet();
+       Ore::cloneTo(def);
+
+       def->column_height_max = column_height_max;
+       def->column_height_min = column_height_min;
+       def->column_midpoint_factor = column_midpoint_factor;
+
+       return def;
+}
+
+
 void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
        v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -221,6 +270,20 @@ OrePuff::~OrePuff()
 }
 
 
+ObjDef *OrePuff::clone() const
+{
+       auto def = new OrePuff();
+       Ore::cloneTo(def);
+
+       def->np_puff_top = np_puff_top;
+       def->np_puff_bottom = np_puff_bottom;
+       def->noise_puff_top = nullptr; // cannot be shared, on-demand
+       def->noise_puff_bottom = nullptr;
+
+       return def;
+}
+
+
 void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
        v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -294,6 +357,14 @@ void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *OreBlob::clone() const
+{
+       auto def = new OreBlob();
+       Ore::cloneTo(def);
+       return def;
+}
+
+
 void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
        v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -366,6 +437,19 @@ OreVein::~OreVein()
 }
 
 
+ObjDef *OreVein::clone() const
+{
+       auto def = new OreVein();
+       Ore::cloneTo(def);
+
+       def->random_factor = random_factor;
+       def->noise2 = nullptr; // cannot be shared, on-demand
+       def->sizey_prev = sizey_prev;
+
+       return def;
+}
+
+
 void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
        v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -434,6 +518,19 @@ OreStratum::~OreStratum()
 }
 
 
+ObjDef *OreStratum::clone() const
+{
+       auto def = new OreStratum();
+       Ore::cloneTo(def);
+
+       def->np_stratum_thickness = np_stratum_thickness;
+       def->noise_stratum_thickness = nullptr; // cannot be shared, on-demand
+       def->stratum_thickness = stratum_thickness;
+
+       return def;
+}
+
+
 void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed,
        v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
index d89360c3c1ca307a8c81af66a7872276153cb1b9..213bdc964e29857ee8d0a78afe87c350f518b3d8 100644 (file)
@@ -74,12 +74,17 @@ public:
        size_t placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
        virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
                v3s16 nmin, v3s16 nmax, u8 *biomemap) = 0;
+
+protected:
+       void cloneTo(Ore *def) const;
 };
 
 class OreScatter : public Ore {
 public:
        static const bool NEEDS_NOISE = false;
 
+       ObjDef *clone() const;
+
        virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
                v3s16 nmin, v3s16 nmax, u8 *biomemap);
 };
@@ -88,6 +93,8 @@ class OreSheet : public Ore {
 public:
        static const bool NEEDS_NOISE = true;
 
+       ObjDef *clone() const;
+
        u16 column_height_min;
        u16 column_height_max;
        float column_midpoint_factor;
@@ -100,6 +107,8 @@ class OrePuff : public Ore {
 public:
        static const bool NEEDS_NOISE = true;
 
+       ObjDef *clone() const;
+
        NoiseParams np_puff_top;
        NoiseParams np_puff_bottom;
        Noise *noise_puff_top = nullptr;
@@ -116,6 +125,8 @@ class OreBlob : public Ore {
 public:
        static const bool NEEDS_NOISE = true;
 
+       ObjDef *clone() const;
+
        virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
                v3s16 nmin, v3s16 nmax, u8 *biomemap);
 };
@@ -124,6 +135,8 @@ class OreVein : public Ore {
 public:
        static const bool NEEDS_NOISE = true;
 
+       ObjDef *clone() const;
+
        float random_factor;
        Noise *noise2 = nullptr;
        int sizey_prev = 0;
@@ -139,6 +152,8 @@ class OreStratum : public Ore {
 public:
        static const bool NEEDS_NOISE = false;
 
+       ObjDef *clone() const;
+
        NoiseParams np_stratum_thickness;
        Noise *noise_stratum_thickness = nullptr;
        u16 stratum_thickness;
@@ -155,6 +170,8 @@ public:
        OreManager(IGameDef *gamedef);
        virtual ~OreManager() = default;
 
+       OreManager *clone() const;
+
        const char *getObjectTitle() const
        {
                return "ore";
@@ -183,4 +200,7 @@ public:
        void clear();
 
        size_t placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+
+private:
+       OreManager() {};
 };
index c1acbfd9d6f370fab4b1b5c5612402a2881823a8..2e04d30e6de4714f4481a025f89f62f74b7eb137 100644 (file)
@@ -77,6 +77,11 @@ Schematic::~Schematic()
        delete []slice_probs;
 }
 
+ObjDef *Schematic::clone() const
+{
+       FATAL_ERROR("not cloneable");
+}
+
 
 void Schematic::resolveNodeNames()
 {
@@ -93,6 +98,7 @@ void Schematic::resolveNodeNames()
 
 void Schematic::blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place)
 {
+       assert(schemdata && slice_probs);
        sanity_check(m_ndef != NULL);
 
        int xstride = 1;
@@ -177,7 +183,7 @@ bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags,
        Rotation rot, bool force_place)
 {
        assert(vm != NULL);
-       assert(schemdata != NULL);
+       assert(schemdata && slice_probs);
        sanity_check(m_ndef != NULL);
 
        //// Determine effective rotation and effective schematic dimensions
index 371b37557eb2e0fefe7613768eb3875c0b8768b7..770f7405336a4f46076414f6133dd7bf9467647a 100644 (file)
@@ -95,6 +95,8 @@ public:
        Schematic();
        virtual ~Schematic();
 
+       ObjDef *clone() const;
+
        virtual void resolveNodeNames();
 
        bool loadSchematicFromFile(const std::string &filename,
@@ -128,6 +130,8 @@ public:
        SchematicManager(Server *server);
        virtual ~SchematicManager() = default;
 
+       // not cloneable
+
        virtual void clear();
 
        const char *getObjectTitle() const
index 37332c3c60928488052817ad5464440484db3781..65199830fe009d34242082c14798f6b521908247 100644 (file)
@@ -1572,6 +1572,18 @@ NodeResolver::~NodeResolver()
 }
 
 
+void NodeResolver::cloneTo(NodeResolver *res) const
+{
+       FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
+               " after resolving has completed");
+       /* We don't actually do anything significant. Since the node resolving has
+        * already completed, the class that called us will already have the
+        * resolved IDs in its data structures (which it copies on its own) */
+       res->m_ndef = m_ndef;
+       res->m_resolve_done = true;
+}
+
+
 void NodeResolver::nodeResolveInternal()
 {
        m_nodenames_idx   = 0;
index c77d53324f51f66ecbbb732f9ea5b3f923c7f909..0fce6eab14a3f59ed9acb290cfc3ac5b285c08f1 100644 (file)
@@ -739,6 +739,9 @@ public:
        virtual ~NodeResolver();
        virtual void resolveNodeNames() = 0;
 
+       // required because this class is used as mixin for ObjDef
+       void cloneTo(NodeResolver *res) const;
+
        bool getIdFromNrBacklog(content_t *result_out,
                const std::string &node_alt, content_t c_fallback,
                bool error_on_fallback = true);
index 08d6844fc135487818ffc73f8a33bcba49e90b32..482544d37f884642a8f2d54db46c13beffce1495 100644 (file)
@@ -182,3 +182,22 @@ bool ObjDefManager::decodeHandle(ObjDefHandle handle, u32 *index,
        *uid   = get_bits(handle, 24, 7);
        return true;
 }
+
+// Cloning
+
+void ObjDef::cloneTo(ObjDef *def) const
+{
+       def->index = index;
+       def->uid = uid;
+       def->handle = handle;
+       def->name = name;
+}
+
+void ObjDefManager::cloneTo(ObjDefManager *mgr) const
+{
+       mgr->m_ndef = m_ndef;
+       mgr->m_objects.reserve(m_objects.size());
+       for (const auto &obj : m_objects)
+               mgr->m_objects.push_back(obj->clone());
+       mgr->m_objtype = m_objtype;
+}
index 9ab3df9775e41a6e3a7022a99898ca2629942771..20565029cb199f98d4f5121f8465db3de15f60e7 100644 (file)
@@ -45,10 +45,22 @@ class ObjDef {
 public:
        virtual ~ObjDef() = default;
 
+       // Only implemented by child classes (leafs in class hierarchy)
+       // Should create new object of its own type, call cloneTo() of parent class
+       // and copy its own instance variables over
+       virtual ObjDef *clone() const = 0;
+
        u32 index;
        u32 uid;
        ObjDefHandle handle;
        std::string name;
+
+protected:
+       // Only implemented by classes that have children themselves
+       // by copying the defintion and changing that argument type (!!!)
+       // Should defer to parent class cloneTo() if applicable and then copy
+       // over its own properties
+       void cloneTo(ObjDef *def) const;
 };
 
 // WARNING: Ownership of ObjDefs is transferred to the ObjDefManager it is
@@ -60,6 +72,8 @@ public:
        virtual ~ObjDefManager();
        DISABLE_CLASS_COPY(ObjDefManager);
 
+       // T *clone() const; // implemented in child class with correct type
+
        virtual const char *getObjectTitle() const { return "ObjDef"; }
 
        virtual void clear();
@@ -88,6 +102,10 @@ public:
                ObjDefType *type, u32 *uid);
 
 protected:
+       ObjDefManager() {};
+       // Helper for child classes to implement clone()
+       void cloneTo(ObjDefManager *mgr) const;
+
        const NodeDefManager *m_ndef;
        std::vector<ObjDef *> m_objects;
        ObjDefType m_objtype;
index c2acdcfe7c1c6c26bdfc806c43434423ab7b1f82..40f7faa9d72961406c47fb28dfd34c541262391b 100644 (file)
@@ -32,6 +32,7 @@ public:
 
        void testHandles();
        void testAddGetSetClear();
+       void testClone();
 };
 
 static TestObjDef g_test_instance;
@@ -40,10 +41,42 @@ void TestObjDef::runTests(IGameDef *gamedef)
 {
        TEST(testHandles);
        TEST(testAddGetSetClear);
+       TEST(testClone);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
+/* Minimal implementation of ObjDef and ObjDefManager subclass */
+
+class MyObjDef : public ObjDef
+{
+public:
+       ObjDef *clone() const
+       {
+               auto def = new MyObjDef();
+               ObjDef::cloneTo(def);
+               def->testvalue = testvalue;
+               return def;
+       };
+
+       u32 testvalue;
+};
+
+class MyObjDefManager : public ObjDefManager
+{
+public:
+       MyObjDefManager(ObjDefType type) : ObjDefManager(NULL, type){};
+       MyObjDefManager *clone() const
+       {
+               auto mgr = new MyObjDefManager();
+               ObjDefManager::cloneTo(mgr);
+               return mgr;
+       };
+
+protected:
+       MyObjDefManager(){};
+};
+
 void TestObjDef::testHandles()
 {
        u32 uid = 0;
@@ -69,25 +102,25 @@ void TestObjDef::testAddGetSetClear()
 
        UASSERTEQ(ObjDefType, testmgr.getType(), OBJDEF_GENERIC);
 
-       obj0 = new ObjDef;
+       obj0 = new MyObjDef;
        obj0->name = "foobar";
        hObj0 = testmgr.add(obj0);
        UASSERT(hObj0 != OBJDEF_INVALID_HANDLE);
        UASSERTEQ(u32, obj0->index, 0);
 
-       obj1 = new ObjDef;
+       obj1 = new MyObjDef;
        obj1->name = "FooBaz";
        hObj1 = testmgr.add(obj1);
        UASSERT(hObj1 != OBJDEF_INVALID_HANDLE);
        UASSERTEQ(u32, obj1->index, 1);
 
-       obj2 = new ObjDef;
+       obj2 = new MyObjDef;
        obj2->name = "asdf";
        hObj2 = testmgr.add(obj2);
        UASSERT(hObj2 != OBJDEF_INVALID_HANDLE);
        UASSERTEQ(u32, obj2->index, 2);
 
-       obj3 = new ObjDef;
+       obj3 = new MyObjDef;
        obj3->name = "foobaz";
        hObj3 = testmgr.add(obj3);
        UASSERT(hObj3 == OBJDEF_INVALID_HANDLE);
@@ -104,3 +137,38 @@ void TestObjDef::testAddGetSetClear()
        testmgr.clear();
        UASSERTEQ(size_t, testmgr.getNumObjects(), 0);
 }
+
+void TestObjDef::testClone()
+{
+       MyObjDefManager testmgr(OBJDEF_GENERIC);
+       ObjDefManager *mgrcopy;
+       MyObjDef *obj, *temp2;
+       ObjDef *temp1;
+       ObjDefHandle hObj;
+
+       obj = new MyObjDef;
+       obj->testvalue = 0xee00ff11;
+       hObj = testmgr.add(obj);
+       UASSERT(hObj != OBJDEF_INVALID_HANDLE);
+
+       mgrcopy = testmgr.clone();
+       UASSERT(mgrcopy);
+       UASSERTEQ(ObjDefType, mgrcopy->getType(), testmgr.getType());
+       UASSERTEQ(size_t, mgrcopy->getNumObjects(), testmgr.getNumObjects());
+
+       // 1) check that the same handle is still valid on the copy
+       temp1 = mgrcopy->get(hObj);
+       UASSERT(temp1);
+       UASSERT(temp1 == mgrcopy->getRaw(0));
+       // 2) check that the copy has the correct C++ class
+       temp2 = dynamic_cast<MyObjDef *>(temp1);
+       UASSERT(temp2);
+       // 3) check that it was correctly copied
+       UASSERTEQ(u32, obj->testvalue, temp2->testvalue);
+       // 4) check that it was copied AT ALL (not the same)
+       UASSERT(obj != temp2);
+
+       testmgr.clear();
+       mgrcopy->clear();
+       delete mgrcopy;
+}