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.ExpiredCacheReloader_test;
18 
19 
20 import ocean.util.container.cache.ExpiredCacheReloader;
21 import ocean.util.serialize.contiguous;
22 
23 import ocean.io.select.EpollSelectDispatcher,
24        ocean.io.select.client.TimerEvent;
25 
26 import ocean.core.Test;
27 
28 import core.sys.posix.time;
29 
30 /******************************************************************************
31 
32     Trivial struct type to imitate stored value
33 
34 ******************************************************************************/
35 
36 struct Trivial
37 {
38     int field;
39 }
40 
41 /******************************************************************************
42 
43     Simulates the real time for the cache below.
44 
45 ******************************************************************************/
46 
47 private time_t now = 0;
48 
49 /******************************************************************************
50 
51     Advances the simulated real time.
52 
53     Params:
54         seconds = amount of seconds to advance the simulated real time
55 
56 ******************************************************************************/
57 
58 private void wait(long seconds)
59 {
60     now += seconds;
61 }
62 
63 /******************************************************************************
64 
65     CachingStructLoader mock implementation
66 
67 ******************************************************************************/
68 
69 class TestCache(S) : ExpiredCacheReloader!(S)
70 {
71     alias typeof(super) Super;
72 
73     static class Cache: Super.Cache
74     {
75         this ( )
76         {
77             super(5, 1); // max 5 items, 1 second
78         }
79 
80         /// Use the simulated time rather than the real time clock.
81 
82         override protected time_t now ( )
83         {
84             return .now;
85         }
86     }
87 
88     // data source for missing records
89     void[][hash_t] source;
90 
91     bool add_empty;
92 
93     void addToSource(hash_t key, S value)
94     {
95         auto elem = key in this.source;
96         if (elem is null)
97             this.source[key] = null;
98         Serializer.serialize(value, this.source[key]);
99     }
100 
101     this (Cache cache)
102     {
103         super(cache);
104         this.add_empty = true;
105     }
106 
107     void addEmptyValues(bool newval)
108     {
109         this.add_empty = newval;
110     }
111 
112     override protected void getData ( hash_t key, scope void delegate ( Contiguous!(S) data ) got )
113     {
114         auto data = key in this.source;
115         if (data)
116         {
117             auto instance = Deserializer.deserialize!(S)(*data);
118             got(instance);
119         }
120         else
121         {
122             if (this.add_empty)
123             {
124                 got(Contiguous!(S)(null));
125             }
126         }
127     }
128 }
129 
130 /******************************************************************************
131 
132     Various test related objects/types reused by all test cases
133 
134 ******************************************************************************/
135 
136 alias TestCache!(Trivial) Cache;
137 
138 private Cache.Cache cache_storage;
139 
140 private Cache cache;
141 
142 static this()
143 {
144     cache_storage = new Cache.Cache;
145     cache = new Cache(cache_storage);
146 }
147 
148 /******************************************************************************
149 
150     Cleans all global state. Called in the beginning of each new test case.
151 
152 ******************************************************************************/
153 
154 private void reset()
155 {
156     cache_storage.clear();
157     cache.source = null;
158 }
159 
160 /******************************************************************************
161 
162     Tests
163 
164 ******************************************************************************/
165 
166 // Empty cache
167 unittest
168 {
169     reset();
170 
171     auto result = 42 in cache;
172     test!("is")(result, null);
173 }
174 
175 // Missing item cached
176 unittest
177 {
178     reset();
179 
180     auto result = 42 in cache;
181     cache.addToSource(42, Trivial(42));
182     result = 42 in cache;
183     test!("is")(result, null);
184 }
185 
186 // Missing item cached not cached if (add_empty_values == false)
187 unittest
188 {
189     reset();
190     cache.addEmptyValues(false);
191     scope(exit) cache.addEmptyValues(true);
192 
193     auto result = 42 in cache;
194     test!("is")(result, null);
195     cache.addToSource(42, Trivial(42));
196     result = 42 in cache;
197     test!("!is")(result, null);
198 }
199 
200 // Missing item updated after timeout
201 unittest
202 {
203     reset();
204 
205     auto result = 42 in cache;
206     cache.addToSource(42, Trivial(42));
207     result = 42 in cache;
208 
209     wait(2);
210     result = 42 in cache;
211     test!("!is")(result, null);
212     test!("==")(result.field, 42);
213 }
214 
215 // Exists on first access
216 unittest
217 {
218     reset();
219 
220     cache.addToSource(43, Trivial(43));
221     auto result = 43 in cache;
222     test!("!is")(result, null);
223     test!("==")(result.field, 43);
224 
225     // also verify persistent identity
226     auto result2 = 43 in cache;
227     test!("is")(result, result2);
228 }