1 /******************************************************************************
2
3 Kept with "_slowtest" suffix because sleep is used to ensure cache
4 invalidation timeout works.
5
6 Copyright:
7 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
8 All rights reserved.
9
10 License:
11 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
12 Alternatively, this file may be distributed under the terms of the Tango
13 3-Clause BSD License (see LICENSE_BSD.txt for details).
14
15 *******************************************************************************/
16
17 module ocean.util.container.cache.CachingStructLoader_test;
18
19
20 import ocean.util.container.cache.CachingStructLoader,
21 ocean.util.container.cache.ExpiringCache;
22 import ocean.util.serialize.contiguous;
23
24 import ocean.io.select.EpollSelectDispatcher,
25 ocean.io.select.client.TimerEvent;
26
27 import core.sys.posix.time;
28
29 version (unittest)
30 {
31 import ocean.core.Test;
32 }
33
34
35 /******************************************************************************
36
37 Trivial struct type to imitate stored value
38
39 ******************************************************************************/
40
41 struct Trivial
42 {
43 int field;
44 }
45
46 /******************************************************************************
47
48 Simulates the real time for the cache below.
49
50 ******************************************************************************/
51
52 private time_t now = 0;
53
54 /******************************************************************************
55
56 Advances the simulated real time.
57
58 Params:
59 seconds = amount of seconds to advance the simulated real time
60
61 ******************************************************************************/
62
63 private void wait(long seconds)
64 {
65 now += seconds;
66 }
67
68 /******************************************************************************
69
70 CachingStructLoader mock implementation
71
72 ******************************************************************************/
73
74 class TestCache(S) : CachingStructLoader!(S)
75 {
76 alias typeof(super) Super;
77
78 static class Cache: Super.Cache
79 {
80 this ( )
81 {
82 super(5, 1); // max 5 items, 1 second
83 }
84
85 /// Use the simulated time rather than the real time clock.
86
87 override protected time_t now ( )
88 {
89 return .now;
90 }
91 }
92
93 // data source for missing records
94 void[][hash_t] source;
95
96 bool add_empty;
97
98 void addToSource(hash_t key, S value)
99 {
100 auto elem = key in this.source;
101 if (elem is null)
102 this.source[key] = null;
103 Serializer.serialize(value, this.source[key]);
104 }
105
106 this (Cache cache)
107 {
108 super(cache);
109 this.add_empty = true;
110 }
111
112 void addEmptyValues(bool newval)
113 {
114 this.add_empty = newval;
115 }
116
117 override protected void getData ( hash_t key, scope void delegate ( Contiguous!(S) data ) got )
118 {
119 auto data = key in this.source;
120 if (data)
121 {
122 auto instance = Deserializer.deserialize!(S)(*data);
123 got(instance);
124 }
125 else
126 {
127 if (this.add_empty)
128 {
129 got(Contiguous!(S)(null));
130 }
131 }
132 }
133 }
134
135 /******************************************************************************
136
137 Various test related objects/types reused by all test cases
138
139 ******************************************************************************/
140
141 alias TestCache!(Trivial) Cache;
142
143 private Cache.Cache cache_storage;
144
145 private Cache cache;
146
147 static this()
148 {
149 cache_storage = new Cache.Cache;
150 cache = new Cache(cache_storage);
151 }
152
153 /******************************************************************************
154
155 Cleans all global state. Called in the beginning of each new test case.
156
157 ******************************************************************************/
158
159 private void reset()
160 {
161 cache_storage.clear();
162 cache.source = null;
163 }
164
165 /******************************************************************************
166
167 Tests
168
169 ******************************************************************************/
170
171 // Empty cache
172 unittest
173 {
174 reset();
175
176 auto result = 42 in cache;
177 test!("is")(result, null);
178 }
179
180 // Missing item cached
181 unittest
182 {
183 reset();
184
185 auto result = 42 in cache;
186 cache.addToSource(42, Trivial(42));
187 result = 42 in cache;
188 test!("is")(result, null);
189 }
190
191 // Missing item cached not cached if (add_empty_values == false)
192 unittest
193 {
194 reset();
195 cache.addEmptyValues(false);
196 scope(exit) cache.addEmptyValues(true);
197
198 auto result = 42 in cache;
199 test!("is")(result, null);
200 cache.addToSource(42, Trivial(42));
201 result = 42 in cache;
202 test!("!is")(result, null);
203 }
204
205 // Missing item updated after timeout
206 unittest
207 {
208 reset();
209
210 auto result = 42 in cache;
211 cache.addToSource(42, Trivial(42));
212 result = 42 in cache;
213
214 wait(2);
215 result = 42 in cache;
216 test!("!is")(result, null);
217 test!("==")(result.field, 42);
218 }
219
220 // Exists on first access
221 unittest
222 {
223 reset();
224
225 cache.addToSource(43, Trivial(43));
226 auto result = 43 in cache;
227 test!("!is")(result, null);
228 test!("==")(result.field, 43);
229
230 // also verify persistent identity
231 auto result2 = 43 in cache;
232 test!("is")(result, result2);
233 }
234
235 // Test clear function
236 unittest
237 {
238 reset();
239
240 cache.add_empty = false; // Searching a non existing value will not add
241 // any null value in the TestCache.source.
242
243 {
244 auto result = 44 in cache;
245 test!("is")(result, null);
246 }
247
248 {
249 cache.addToSource(44, Trivial(44));
250 auto result = 44 in cache;
251 test!("!is")(result, null);
252 test!("==")(result.field, 44);
253 }
254
255 // Remove the item from the TestCache.source. This means that the item
256 // does not exist in the external storage, but it exists in the cache.
257 cache.source.remove(44);
258 {
259 auto result = 44 in cache;
260 test!("!is")(result, null);
261 test!("==")(result.field, 44);
262 }
263
264 // Now, clean the cache. The object should not exist anywhere.
265 cache.clear();
266 {
267 auto result = 44 in cache;
268 test!("is")(result, null);
269 }
270 }