[go: up one dir, main page]

1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2016 StatPro Italia srl
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include "lazyobject.hpp"
21#include "utilities.hpp"
22#include <ql/instruments/stock.hpp>
23#include <ql/quotes/simplequote.hpp>
24
25using namespace QuantLib;
26using namespace boost::unit_test_framework;
27using ext::shared_ptr;
28
29namespace lazy_object_test {
30
31 class TearDown { // NOLINT(cppcoreguidelines-special-member-functions)
32 bool alwaysForward;
33 public:
34 TearDown() : alwaysForward(LazyObject::Defaults::instance().forwardsAllNotifications()) {}
35 ~TearDown() {
36 if (alwaysForward)
37 LazyObject::Defaults::instance().alwaysForwardNotifications();
38 else
39 LazyObject::Defaults::instance().forwardFirstNotificationOnly();
40 }
41 };
42
43}
44
45void LazyObjectTest::testDiscardingNotifications() {
46
47 BOOST_TEST_MESSAGE("Testing that lazy objects can discard notifications after the first against default...");
48
49 lazy_object_test::TearDown teardown;
50
51 LazyObject::Defaults::instance().alwaysForwardNotifications();
52
53 ext::shared_ptr<SimpleQuote> q(new SimpleQuote(0.0));
54 ext::shared_ptr<Instrument> s(new Stock(Handle<Quote>(q)));
55
56 Flag f;
57 f.registerWith(h: s);
58
59 s->forwardFirstNotificationOnly();
60
61 s->NPV();
62 q->setValue(1.0);
63 if (!f.isUp())
64 BOOST_FAIL("Observer was not notified of change");
65
66 f.lower();
67 q->setValue(2.0);
68 if (f.isUp())
69 BOOST_FAIL("Observer was notified of second change");
70
71 f.lower();
72 s->NPV();
73 q->setValue(3.0);
74 if (!f.isUp())
75 BOOST_FAIL("Observer was not notified of change after recalculation");
76}
77
78
79void LazyObjectTest::testDiscardingNotificationsByDefault() {
80
81 BOOST_TEST_MESSAGE("Testing that lazy objects can discard notifications after the first by default...");
82
83 lazy_object_test::TearDown teardown;
84
85 LazyObject::Defaults::instance().forwardFirstNotificationOnly();
86
87 ext::shared_ptr<SimpleQuote> q(new SimpleQuote(0.0));
88 ext::shared_ptr<Instrument> s(new Stock(Handle<Quote>(q)));
89
90 Flag f;
91 f.registerWith(h: s);
92
93 s->NPV();
94 q->setValue(1.0);
95 if (!f.isUp())
96 BOOST_FAIL("Observer was not notified of change");
97
98 f.lower();
99 q->setValue(2.0);
100 if (f.isUp())
101 BOOST_FAIL("Observer was notified of second change");
102
103 f.lower();
104 s->NPV();
105 q->setValue(3.0);
106 if (!f.isUp())
107 BOOST_FAIL("Observer was not notified of change after recalculation");
108}
109
110
111void LazyObjectTest::testForwardingNotificationsByDefault() {
112
113 BOOST_TEST_MESSAGE("Testing that lazy objects can forward all notifications by default...");
114
115 lazy_object_test::TearDown teardown;
116
117 LazyObject::Defaults::instance().alwaysForwardNotifications();
118
119 ext::shared_ptr<SimpleQuote> q(new SimpleQuote(0.0));
120 ext::shared_ptr<Instrument> s(new Stock(Handle<Quote>(q)));
121
122 Flag f;
123 f.registerWith(h: s);
124
125 s->NPV();
126 q->setValue(1.0);
127 if (!f.isUp())
128 BOOST_FAIL("Observer was not notified of change");
129
130 f.lower();
131 q->setValue(2.0);
132 if (!f.isUp())
133 BOOST_FAIL("Observer was not notified of second change");
134}
135
136void LazyObjectTest::testForwardingNotifications() {
137
138 BOOST_TEST_MESSAGE("Testing that lazy objects can forward all notifications against default...");
139
140 lazy_object_test::TearDown teardown;
141
142 LazyObject::Defaults::instance().forwardFirstNotificationOnly();
143
144 ext::shared_ptr<SimpleQuote> q(new SimpleQuote(0.0));
145 ext::shared_ptr<Instrument> s(new Stock(Handle<Quote>(q)));
146
147 Flag f;
148 f.registerWith(h: s);
149
150 s->alwaysForwardNotifications();
151
152 s->NPV();
153 q->setValue(1.0);
154 if (!f.isUp())
155 BOOST_FAIL("Observer was not notified of change");
156
157 f.lower();
158 q->setValue(2.0);
159 if (!f.isUp())
160 BOOST_FAIL("Observer was not notified of second change");
161}
162
163void LazyObjectTest::testNotificationLoop() {
164
165 BOOST_TEST_MESSAGE("Testing that lazy objects manage recursive notifications...");
166
167 lazy_object_test::TearDown teardown;
168
169 LazyObject::Defaults::instance().alwaysForwardNotifications();
170
171 auto q = ext::make_shared<SimpleQuote>(args: 0.0);
172 auto s1 = ext::make_shared<Stock>(args: Handle<Quote>(q));
173 auto s2 = ext::make_shared<Stock>(args: Handle<Quote>());
174 auto s3 = ext::make_shared<Stock>(args: Handle<Quote>());
175
176 s3->registerWith(h: s2);
177 s2->registerWith(h: s1);
178 s1->registerWith(h: s3);
179
180#ifdef QL_THROW_IN_CYCLES
181
182 BOOST_CHECK_EXCEPTION(q->setValue(2.0), Error,
183 ExpectedErrorMessage("recursive notification loop detected"));
184
185#else
186
187 Flag f;
188 f.registerWith(h: s3);
189 q->setValue(2.0);
190
191 if (!f.isUp())
192 BOOST_FAIL("Observer was not notified of change");
193
194#endif
195
196 // We have produced a ring of dependencies which we break here
197 // see https://github.com/lballabio/QuantLib/issues/1725
198 s1->unregisterWithAll();
199 s2->unregisterWithAll();
200 s3->unregisterWithAll();
201}
202
203test_suite* LazyObjectTest::suite() {
204 auto* suite = BOOST_TEST_SUITE("LazyObject tests");
205 suite->add(QUANTLIB_TEST_CASE(&LazyObjectTest::testDiscardingNotifications));
206 suite->add(QUANTLIB_TEST_CASE(&LazyObjectTest::testDiscardingNotificationsByDefault));
207 suite->add(QUANTLIB_TEST_CASE(&LazyObjectTest::testForwardingNotificationsByDefault));
208 suite->add(QUANTLIB_TEST_CASE(&LazyObjectTest::testForwardingNotifications));
209 suite->add(QUANTLIB_TEST_CASE(&LazyObjectTest::testNotificationLoop));
210 return suite;
211}
212

source code of quantlib/test-suite/lazyobject.cpp