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 }