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 }