mirror of
https://github.com/AdaCore/cvc5.git
synced 2026-02-12 12:32:16 -08:00
211 lines
5.0 KiB
C++
211 lines
5.0 KiB
C++
/******************************************************************************
|
|
* Top contributors (to current version):
|
|
* Aina Niemetz, Morgan Deters, Andres Noetzli
|
|
*
|
|
* This file is part of the cvc5 project.
|
|
*
|
|
* Copyright (c) 2009-2025 by the authors listed in the file AUTHORS
|
|
* in the top-level source directory and their institutional affiliations.
|
|
* All rights reserved. See the file COPYING in the top-level source
|
|
* directory for licensing information.
|
|
* ****************************************************************************
|
|
*
|
|
* Black box testing of cvc5::context::Context.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
#include "base/exception.h"
|
|
#include "context/cdlist.h"
|
|
#include "context/cdo.h"
|
|
#include "test_context.h"
|
|
|
|
namespace cvc5::internal {
|
|
|
|
using namespace context;
|
|
|
|
namespace test {
|
|
|
|
struct MyContextNotifyObj : public ContextNotifyObj
|
|
{
|
|
MyContextNotifyObj(Context* context, bool pre)
|
|
: ContextNotifyObj(context, pre), d_ncalls(0)
|
|
{
|
|
}
|
|
|
|
~MyContextNotifyObj() override {}
|
|
|
|
void contextNotifyPop() override { ++d_ncalls; }
|
|
|
|
int32_t d_ncalls;
|
|
};
|
|
|
|
class MyContextObj : public ContextObj
|
|
{
|
|
public:
|
|
MyContextObj(Context* context, MyContextNotifyObj& n)
|
|
: ContextObj(context), d_ncalls(0), d_nsaves(0), d_notify(n)
|
|
{
|
|
}
|
|
|
|
~MyContextObj() override { destroy(); }
|
|
|
|
ContextObj* save(ContextMemoryManager* pcmm) override
|
|
{
|
|
++d_nsaves;
|
|
return new (pcmm) MyContextObj(*this);
|
|
}
|
|
|
|
void restore(ContextObj* contextObj) override
|
|
{
|
|
d_ncalls = d_notify.d_ncalls;
|
|
}
|
|
|
|
void makeCurrent() { ContextObj::makeCurrent(); }
|
|
|
|
int32_t d_ncalls;
|
|
int32_t d_nsaves;
|
|
|
|
private:
|
|
MyContextObj(const MyContextObj& other)
|
|
: ContextObj(other), d_ncalls(0), d_nsaves(0), d_notify(other.d_notify)
|
|
{
|
|
}
|
|
MyContextNotifyObj& d_notify;
|
|
};
|
|
|
|
class TestContextBlack : public TestContext
|
|
{
|
|
};
|
|
|
|
TEST_F(TestContextBlack, push_pop)
|
|
{
|
|
// Test what happens when the context is popped below 0
|
|
// the interface doesn't declare any exceptions
|
|
d_context->push();
|
|
d_context->pop();
|
|
#ifdef CVC5_ASSERTIONS
|
|
ASSERT_DEATH(d_context->pop(), "Cannot pop below level 0");
|
|
ASSERT_DEATH(d_context->pop(), "Cannot pop below level 0");
|
|
#endif /* CVC5_ASSERTIONS */
|
|
}
|
|
|
|
TEST_F(TestContextBlack, dtor)
|
|
{
|
|
// Destruction of ContextObj was broken in revision 324 (bug #45) when
|
|
// at a higher context level with an intervening modification.
|
|
// (The following caused a "pure virtual method called" error.)
|
|
CDO<int32_t> i(d_context.get());
|
|
d_context->push();
|
|
i = 5;
|
|
}
|
|
|
|
TEST_F(TestContextBlack, pre_post_notify)
|
|
{
|
|
// This is tricky; we want to detect if pre- and post-notifies are
|
|
// done correctly. For that, we have to use a special ContextObj,
|
|
// since that's the only thing that runs between pre- and post-.
|
|
|
|
MyContextNotifyObj a(d_context.get(), true), b(d_context.get(), false);
|
|
|
|
try
|
|
{
|
|
MyContextNotifyObj c(d_context.get(), true), d(d_context.get(), false);
|
|
|
|
ASSERT_EQ(a.d_ncalls, 0);
|
|
ASSERT_EQ(b.d_ncalls, 0);
|
|
ASSERT_EQ(c.d_ncalls, 0);
|
|
ASSERT_EQ(d.d_ncalls, 0);
|
|
|
|
MyContextObj w(d_context.get(), a);
|
|
MyContextObj x(d_context.get(), b);
|
|
MyContextObj y(d_context.get(), c);
|
|
MyContextObj z(d_context.get(), d);
|
|
|
|
d_context->push();
|
|
|
|
w.makeCurrent();
|
|
x.makeCurrent();
|
|
y.makeCurrent();
|
|
z.makeCurrent();
|
|
|
|
ASSERT_EQ(a.d_ncalls, 0);
|
|
ASSERT_EQ(b.d_ncalls, 0);
|
|
ASSERT_EQ(c.d_ncalls, 0);
|
|
ASSERT_EQ(d.d_ncalls, 0);
|
|
|
|
ASSERT_EQ(w.d_ncalls, 0);
|
|
ASSERT_EQ(x.d_ncalls, 0);
|
|
ASSERT_EQ(y.d_ncalls, 0);
|
|
ASSERT_EQ(z.d_ncalls, 0);
|
|
|
|
d_context->push();
|
|
|
|
w.makeCurrent();
|
|
x.makeCurrent();
|
|
y.makeCurrent();
|
|
z.makeCurrent();
|
|
|
|
ASSERT_EQ(a.d_ncalls, 0);
|
|
ASSERT_EQ(b.d_ncalls, 0);
|
|
ASSERT_EQ(c.d_ncalls, 0);
|
|
ASSERT_EQ(d.d_ncalls, 0);
|
|
|
|
ASSERT_EQ(w.d_ncalls, 0);
|
|
ASSERT_EQ(x.d_ncalls, 0);
|
|
ASSERT_EQ(y.d_ncalls, 0);
|
|
ASSERT_EQ(z.d_ncalls, 0);
|
|
|
|
d_context->pop();
|
|
|
|
ASSERT_EQ(a.d_ncalls, 1);
|
|
ASSERT_EQ(b.d_ncalls, 1);
|
|
ASSERT_EQ(c.d_ncalls, 1);
|
|
ASSERT_EQ(d.d_ncalls, 1);
|
|
|
|
ASSERT_EQ(w.d_ncalls, 1);
|
|
ASSERT_EQ(x.d_ncalls, 0);
|
|
ASSERT_EQ(y.d_ncalls, 1);
|
|
ASSERT_EQ(z.d_ncalls, 0);
|
|
|
|
d_context->pop();
|
|
|
|
ASSERT_EQ(a.d_ncalls, 2);
|
|
ASSERT_EQ(b.d_ncalls, 2);
|
|
ASSERT_EQ(c.d_ncalls, 2);
|
|
ASSERT_EQ(d.d_ncalls, 2);
|
|
|
|
ASSERT_EQ(w.d_ncalls, 2);
|
|
ASSERT_EQ(x.d_ncalls, 1);
|
|
ASSERT_EQ(y.d_ncalls, 2);
|
|
ASSERT_EQ(z.d_ncalls, 1);
|
|
}
|
|
catch (Exception& e)
|
|
{
|
|
std::cerr << e.toString() << std::endl;
|
|
ASSERT_TRUE(false) << "Exception thrown from test";
|
|
}
|
|
|
|
// we do this (together with the { } block above) to get full code
|
|
// coverage of destruction paths; a and b haven't been destructed
|
|
// yet, here.
|
|
d_context.reset(nullptr);
|
|
}
|
|
|
|
TEST_F(TestContextBlack, detect_invalid_obj)
|
|
{
|
|
MyContextNotifyObj n(d_context.get(), true);
|
|
|
|
{
|
|
// Objects allocated at the bottom scope are allowed to outlive the scope
|
|
// that they have been allocated in.
|
|
d_context->push();
|
|
MyContextObj x(d_context.get(), n);
|
|
d_context->pop();
|
|
}
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace cvc5::internal
|