1 //
2 // MemoryCacheTest.cs
3 //
4 // Authors:
5 //      Marek Habersack <mhabersack@novell.com>
6 //
7 // Copyright (C) 2010 Novell, Inc. (http://novell.com/)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Collections.ObjectModel;
32 using System.Collections.Specialized;
33 using System.Runtime.Caching;
34 using System.Threading;
35 
36 using NUnit.Framework;
37 using MonoTests.Common;
38 
39 namespace MonoTests.System.Runtime.Caching
40 {
41 	[TestFixture]
42 	public class MemoryCacheTest
43 	{
44 		[Test]
ConstructorParameters()45 		public void ConstructorParameters ()
46 		{
47 			MemoryCache mc;
48 			Assert.Throws<ArgumentNullException> (() => {
49 				mc = new MemoryCache (null);
50 			}, "#A1");
51 
52 			Assert.Throws<ArgumentException> (() => {
53 				mc = new MemoryCache (String.Empty);
54 			}, "#A2");
55 
56 			Assert.Throws<ArgumentException> (() => {
57 				mc = new MemoryCache ("default");
58 			}, "#A3");
59 
60 			var config = new NameValueCollection ();
61 			config.Add ("CacheMemoryLimitMegabytes", "invalid");
62 			Assert.Throws<ArgumentException> (() => {
63 				mc = new MemoryCache ("MyCache", config);
64 			}, "#A4-1");
65 
66 			config.Clear ();
67 			config.Add ("PhysicalMemoryLimitPercentage", "invalid");
68 			Assert.Throws<ArgumentException> (() => {
69 				mc = new MemoryCache ("MyCache", config);
70 			}, "#A4-2");
71 
72 			config.Clear ();
73 			config.Add ("PollingInterval", "invalid");
74 			Assert.Throws<ArgumentException> (() => {
75 				mc = new MemoryCache ("MyCache", config);
76 			}, "#A4-3");
77 
78 			config.Clear ();
79 			config.Add ("CacheMemoryLimitMegabytes", "-1");
80 			Assert.Throws<ArgumentException> (() => {
81 				mc = new MemoryCache ("MyCache", config);
82 			}, "#A4-4");
83 
84 			config.Clear ();
85 			config.Add ("CacheMemoryLimitMegabytes", UInt64.MaxValue.ToString ());
86 			Assert.Throws<ArgumentException> (() => {
87 				mc = new MemoryCache ("MyCache", config);
88 			}, "#A4-5");
89 
90 			config.Clear ();
91 			config.Add ("PhysicalMemoryLimitPercentage", "-1");
92 			Assert.Throws<ArgumentException> (() => {
93 				mc = new MemoryCache ("MyCache", config);
94 			}, "#A4-6");
95 
96 			config.Clear ();
97 			config.Add ("PhysicalMemoryLimitPercentage", UInt64.MaxValue.ToString ());
98 			Assert.Throws<ArgumentException> (() => {
99 				mc = new MemoryCache ("MyCache", config);
100 			}, "#A4-7");
101 
102 			config.Clear ();
103 			config.Add ("PhysicalMemoryLimitPercentage", UInt32.MaxValue.ToString ());
104 			Assert.Throws<ArgumentException> (() => {
105 				mc = new MemoryCache ("MyCache", config);
106 			}, "#A4-8");
107 
108 			config.Clear ();
109 			config.Add ("PhysicalMemoryLimitPercentage", "-10");
110 			Assert.Throws<ArgumentException> (() => {
111 				mc = new MemoryCache ("MyCache", config);
112 			}, "#A4-9");
113 
114 			config.Clear ();
115 			config.Add ("PhysicalMemoryLimitPercentage", "0");
116 			// Just make sure it doesn't throw any exception
117 			mc = new MemoryCache ("MyCache", config);
118 
119 			config.Clear ();
120 			config.Add ("PhysicalMemoryLimitPercentage", "101");
121 			Assert.Throws<ArgumentException> (() => {
122 				mc = new MemoryCache ("MyCache", config);
123 			}, "#A4-10");
124 
125 			// Just make sure it doesn't throw any exception
126 			config.Clear ();
127 			config.Add ("UnsupportedSetting", "123");
128 			mc = new MemoryCache ("MyCache", config);
129 		}
130 
131 		[Test]
Defaults()132 		public void Defaults ()
133 		{
134 			var mc = new MemoryCache ("MyCache");
135 			Assert.AreEqual ("MyCache", mc.Name, "#A1");
136 			// Value of this property is different from system to system
137 			//Assert.AreEqual (0, mc.CacheMemoryLimit, "#A3");
138 			Assert.AreEqual (TimeSpan.FromMinutes (2), mc.PollingInterval, "#A4");
139 			Assert.AreEqual (
140 				DefaultCacheCapabilities.InMemoryProvider |
141 				DefaultCacheCapabilities.CacheEntryChangeMonitors |
142 				DefaultCacheCapabilities.AbsoluteExpirations |
143 				DefaultCacheCapabilities.SlidingExpirations |
144 				DefaultCacheCapabilities.CacheEntryRemovedCallback |
145 				DefaultCacheCapabilities.CacheEntryUpdateCallback,
146 				mc.DefaultCacheCapabilities, "#A1");
147 		}
148 
149 		[Test]
DefaultInstanceDefaults()150 		public void DefaultInstanceDefaults ()
151 		{
152 			var mc = MemoryCache.Default;
153 			Assert.AreEqual ("Default", mc.Name, "#A1");
154 			// Value of this property is different from system to system
155 			//Assert.AreEqual (0, mc.CacheMemoryLimit, "#A3");
156 			Assert.AreEqual (TimeSpan.FromMinutes (2), mc.PollingInterval, "#A4");
157 			Assert.AreEqual (
158 				DefaultCacheCapabilities.InMemoryProvider |
159 				DefaultCacheCapabilities.CacheEntryChangeMonitors |
160 				DefaultCacheCapabilities.AbsoluteExpirations |
161 				DefaultCacheCapabilities.SlidingExpirations |
162 				DefaultCacheCapabilities.CacheEntryRemovedCallback |
163 				DefaultCacheCapabilities.CacheEntryUpdateCallback,
164 				mc.DefaultCacheCapabilities, "#A1");
165 		}
166 
167 		[Test]
ConstructorValues()168 		public void ConstructorValues ()
169 		{
170 			var config = new NameValueCollection ();
171 			config.Add ("CacheMemoryLimitMegabytes", "1");
172 			config.Add ("pollingInterval", "00:10:00");
173 
174 			var mc = new MemoryCache ("MyCache", config);
175 			Assert.AreEqual (1048576, mc.CacheMemoryLimit, "#A2");
176 			Assert.AreEqual (TimeSpan.FromMinutes (10), mc.PollingInterval, "#A3");
177 
178 			config.Clear ();
179 			config.Add ("PhysicalMemoryLimitPercentage", "10");
180 			config.Add ("CacheMemoryLimitMegabytes", "5");
181 			config.Add ("PollingInterval", "01:10:00");
182 
183 			mc = new MemoryCache ("MyCache", config);
184 			Assert.AreEqual (10, mc.PhysicalMemoryLimit, "#B1");
185 			Assert.AreEqual (5242880, mc.CacheMemoryLimit, "#B2");
186 			Assert.AreEqual (TimeSpan.FromMinutes (70), mc.PollingInterval, "#B3");
187 		}
188 
189 		[Test]
Indexer()190 		public void Indexer ()
191 		{
192 			var mc = new PokerMemoryCache ("MyCache");
193 
194 			Assert.Throws<ArgumentNullException> (() => {
195 				mc [null] = "value";
196 			}, "#A1-1");
197 
198 			Assert.Throws<ArgumentNullException> (() => {
199 				object v = mc [null];
200 			}, "#A1-2");
201 
202 			Assert.Throws<ArgumentNullException> (() => {
203 				mc ["key"] = null;
204 			}, "#A1-3");
205 
206 			mc.Calls.Clear ();
207 			mc ["key"] = "value";
208 			Assert.AreEqual (3, mc.Calls.Count, "#A2-1");
209 			Assert.AreEqual ("set_this [string key]", mc.Calls [0], "#A2-2");
210 			Assert.AreEqual ("Set (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls [1], "#A2-3");
211 			Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [2], "#A2-4");
212 			Assert.IsTrue (mc.Contains ("key"), "#A2-5");
213 
214 			mc.Calls.Clear ();
215 			object value = mc ["key"];
216 			Assert.AreEqual (1, mc.Calls.Count, "#A3-1");
217 			Assert.AreEqual ("get_this [string key]", mc.Calls [0], "#A3-2");
218 			Assert.AreEqual ("value", value, "#A3-3");
219 		}
220 
221 		[Test]
Contains()222 		public void Contains ()
223 		{
224 			var mc = new PokerMemoryCache ("MyCache");
225 
226 			Assert.Throws<ArgumentNullException> (() => {
227 				mc.Contains (null);
228 			}, "#A1-1");
229 
230 			Assert.Throws<NotSupportedException> (() => {
231 				mc.Contains ("key", "region");
232 			}, "#A1-2");
233 
234 			mc.Set ("key", "value", ObjectCache.InfiniteAbsoluteExpiration);
235 			Assert.IsTrue (mc.Contains ("key"), "#A2");
236 
237 			var cip = new CacheItemPolicy ();
238 			cip.Priority = CacheItemPriority.NotRemovable;
239 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (500);
240 			mc.Set ("key", "value", cip);
241 			Assert.IsTrue (mc.Contains ("key"), "#B1-1");
242 			Thread.Sleep (1000);
243 			// The call below removes the expired entry and returns false
244 			Assert.IsFalse (mc.Contains ("key"), "#B1-2");
245 		}
246 
247 		[Test]
CreateCacheEntryChangeMonitor()248 		public void CreateCacheEntryChangeMonitor ()
249 		{
250 			var mc = new PokerMemoryCache ("MyCache");
251 
252 			Assert.Throws<NotSupportedException> (() => {
253 				mc.CreateCacheEntryChangeMonitor (new string [] { "key" }, "region");
254 			}, "#A1-1");
255 
256 			Assert.Throws<ArgumentNullException> (() => {
257 				mc.CreateCacheEntryChangeMonitor (null);
258 			}, "#A1-2");
259 
260 			Assert.Throws<ArgumentException> (() => {
261 				mc.CreateCacheEntryChangeMonitor (new string [] {});
262 			}, "#A1-3");
263 
264 			Assert.Throws<ArgumentException> (() => {
265 				mc.CreateCacheEntryChangeMonitor (new string [] { "key", null });
266 			}, "#A1-4");
267 
268 			mc.Set ("key1", "value1", ObjectCache.InfiniteAbsoluteExpiration);
269 			mc.Set ("key2", "value2", ObjectCache.InfiniteAbsoluteExpiration);
270 			mc.Set ("key3", "value3", ObjectCache.InfiniteAbsoluteExpiration);
271 
272 			CacheEntryChangeMonitor monitor = mc.CreateCacheEntryChangeMonitor (new string [] { "key1", "key2" });
273 			Assert.IsNotNull (monitor, "#A2-1");
274 			Assert.AreEqual ("System.Runtime.Caching.MemoryCacheEntryChangeMonitor", monitor.GetType ().ToString (), "#A2-2");
275 			Assert.AreEqual (2, monitor.CacheKeys.Count, "#A2-3");
276 			Assert.AreEqual ("key1", monitor.CacheKeys [0], "#A2-3-1");
277 			Assert.AreEqual ("key2", monitor.CacheKeys [1], "#A2-3-2");
278 			Assert.IsNull (monitor.RegionName, "#A2-4");
279 			// Since this comparison can fail from time to time, leaving it commented out
280 			//Assert.AreEqual (DateTimeOffset.UtcNow.ToString (), monitor.LastModified.ToString (), "#A2-5");
281 			Assert.IsFalse (monitor.HasChanged, "#A2-5");
282 
283 			// The actual unique id is constructed from key names followed by the hex value of ticks of their last modifed time
284 			Assert.IsFalse (String.IsNullOrEmpty (monitor.UniqueId), "#A2-6");
285 
286 			// There seems to be a bug in .NET 4.0 regarding the code below. MSDN says that non-existing keys will cause the
287 			// returned monitor instance to be marked as changed, but instead this exception is thrown:
288 			//
289 			// MonoTests.System.Runtime.Caching.MemoryCacheTest.CreateCacheEntryChangeMonitor:
290 			// System.ArgumentOutOfRangeException : The UTC time represented when the offset is applied must be between year 0 and 10,000.
291 			// Parameter name: offset
292 			//
293 			// at System.DateTimeOffset.ValidateDate(DateTime dateTime, TimeSpan offset)
294 			// at System.DateTimeOffset..ctor(DateTime dateTime)
295 			// at System.Runtime.Caching.MemoryCacheEntryChangeMonitor.InitDisposableMembers(MemoryCache cache)
296 			// at System.Runtime.Caching.MemoryCache.CreateCacheEntryChangeMonitor(IEnumerable`1 keys, String regionName)
297 			// at MonoTests.Common.PokerMemoryCache.CreateCacheEntryChangeMonitor(IEnumerable`1 keys, String regionName) in C:\Users\grendel\documents\visual studio 2010\Projects\System.Runtime.Caching.Test\System.Runtime.Caching.Test\Common\PokerMemoryCache.cs:line 113
298 			// at MonoTests.System.Runtime.Caching.MemoryCacheTest.CreateCacheEntryChangeMonitor() in C:\Users\grendel\documents\visual studio 2010\Projects\System.Runtime.Caching.Test\System.Runtime.Caching.Test\System.Runtime.Caching\MemoryCacheTest.cs:line 275
299 			//
300 			// It's probably caused by the code passing a DateTime.MinValue to DateTimeOffset constructor for non-existing entries.
301 			// Until this (apparent) bug is fixed, Mono is going to implement the buggy behavior.
302 			//
303 #if false
304 			monitor = mc.CreateCacheEntryChangeMonitor (new string [] { "key1", "doesnotexist" });
305 			Assert.IsNotNull (monitor, "#A3-1");
306 			Assert.AreEqual ("System.Runtime.Caching.MemoryCacheEntryChangeMonitor", monitor.GetType ().ToString (), "#A3-2");
307 			Assert.AreEqual (1, monitor.CacheKeys.Count, "#A3-3");
308 			Assert.AreEqual ("key1", monitor.CacheKeys [0], "#A3-3-1");
309 			Assert.IsNull (monitor.RegionName, "#A3-4");
310 			Assert.IsTrue (monitor.HasChanged, "#A3-5");
311 #endif
312 		}
313 
314 		[Test]
AddOrGetExisting_String_Object_DateTimeOffset_String()315 		public void AddOrGetExisting_String_Object_DateTimeOffset_String ()
316 		{
317 			var mc = new PokerMemoryCache ("MyCache");
318 
319 			Assert.Throws<ArgumentNullException> (() => {
320 				mc.AddOrGetExisting (null, "value", DateTimeOffset.Now);
321 			}, "#A1-1");
322 
323 			Assert.Throws<ArgumentNullException> (() => {
324 				mc.AddOrGetExisting ("key", null, DateTimeOffset.Now);
325 			}, "#A1-2");
326 
327 			Assert.Throws<NotSupportedException> (() => {
328 				mc.AddOrGetExisting ("key", "value", DateTimeOffset.Now, "region");
329 			}, "#A1-3");
330 
331 			object value = mc.AddOrGetExisting ("key3_A2-1", "value", DateTimeOffset.Now.AddMinutes (1));
332 			Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A2-1");
333 			Assert.IsNull (value, "#A2-2");
334 
335 			mc.Calls.Clear ();
336 			value = mc.AddOrGetExisting ("key3_A2-1", "value2", DateTimeOffset.Now.AddMinutes (1));
337 			Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A3-1");
338 			Assert.IsNotNull (value, "#A3-2");
339 			Assert.AreEqual ("value", value, "#A3-3");
340 			Assert.AreEqual (2, mc.Calls.Count, "#A3-4");
341 			Assert.AreEqual ("AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls [0], "#A3-5");
342 
343 			value = mc.AddOrGetExisting ("key_expired", "value", DateTimeOffset.MinValue);
344 			Assert.IsFalse (mc.Contains ("key_expired"), "#A4-1");
345 			Assert.IsNull (value, "#A4-1");
346 		}
347 
348 		[Test]
AddOrGetExisting_String_Object_CacheItemPolicy_String()349 		public void AddOrGetExisting_String_Object_CacheItemPolicy_String ()
350 		{
351 			var mc = new PokerMemoryCache ("MyCache");
352 
353 			Assert.Throws<ArgumentNullException> (() => {
354 				mc.AddOrGetExisting (null, "value", null);
355 			}, "#A1-1");
356 
357 			Assert.Throws<ArgumentNullException> (() => {
358 				mc.AddOrGetExisting ("key", null, null);
359 			}, "#A1-2");
360 
361 			var cip = new CacheItemPolicy ();
362 			cip.AbsoluteExpiration = DateTime.Now.AddMinutes (1);
363 			cip.SlidingExpiration = TimeSpan.FromMinutes (1);
364 
365 			Assert.Throws<ArgumentException> (() => {
366 				mc.AddOrGetExisting ("key", "value", cip);
367 			}, "#A1-3");
368 
369 			cip = new CacheItemPolicy ();
370 			cip.SlidingExpiration = TimeSpan.MinValue;
371 			Assert.Throws<ArgumentOutOfRangeException> (() => {
372 				mc.AddOrGetExisting ("key3", "value", cip);
373 			}, "#A1-4");
374 
375 			Assert.Throws<NotSupportedException> (() => {
376 				mc.AddOrGetExisting ("key", "value", null, "region");
377 			}, "#A1-5");
378 
379 			cip = new CacheItemPolicy ();
380 			cip.SlidingExpiration = TimeSpan.FromDays (500);
381 			Assert.Throws<ArgumentOutOfRangeException> (() => {
382 				mc.AddOrGetExisting ("key3", "value", cip);
383 			}, "#A1-6");
384 
385 			cip = new CacheItemPolicy ();
386 			cip.Priority = (CacheItemPriority) 20;
387 			Assert.Throws<ArgumentOutOfRangeException> (() => {
388 				mc.AddOrGetExisting ("key3", "value", cip);
389 			}, "#A1-7");
390 
391 			cip = new CacheItemPolicy ();
392 			cip.SlidingExpiration = TimeSpan.FromTicks (0L);
393 			mc.AddOrGetExisting ("key3_A2-1", "value", cip);
394 			Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A2-1");
395 
396 			cip = new CacheItemPolicy ();
397 			cip.SlidingExpiration = TimeSpan.FromDays (365);
398 			mc.AddOrGetExisting ("key3_A2-2", "value", cip);
399 			Assert.IsTrue (mc.Contains ("key3_A2-2"), "#A2-2");
400 
401 			cip = new CacheItemPolicy ();
402 			cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
403 			object value = mc.AddOrGetExisting ("key3_A2-3", "value", cip);
404 			Assert.IsTrue (mc.Contains ("key3_A2-3"), "#A2-3");
405 			Assert.IsNull (value, "#A2-4");
406 
407 			mc.Calls.Clear ();
408 			value = mc.AddOrGetExisting ("key3_A2-3", "value2", null);
409 			Assert.IsTrue (mc.Contains ("key3_A2-3"), "#A3-1");
410 			Assert.IsNotNull (value, "#A3-2");
411 			Assert.AreEqual ("value", value, "#A3-3");
412 			Assert.AreEqual (2, mc.Calls.Count, "#A3-4");
413 			Assert.AreEqual ("AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [0], "#A3-5");
414 
415 			cip = new CacheItemPolicy ();
416 			cip.AbsoluteExpiration = DateTimeOffset.MinValue;
417 			value = mc.AddOrGetExisting ("key_expired", "value", cip);
418 			Assert.IsFalse (mc.Contains ("key_expired"), "#A4-1");
419 			Assert.IsNull (value, "#A4-1");
420 		}
421 
422 		[Test]
AddOrGetExisting_CacheItem_CacheItemPolicy()423 		public void AddOrGetExisting_CacheItem_CacheItemPolicy ()
424 		{
425 			var mc = new PokerMemoryCache ("MyCache");
426 			CacheItem ci, ci2;
427 
428 			Assert.Throws<ArgumentNullException> (() => {
429 				ci = mc.AddOrGetExisting (null, new CacheItemPolicy ());
430 			}, "#A1");
431 
432 			ci = new CacheItem ("key", "value");
433 			ci2 = mc.AddOrGetExisting (ci, null);
434 
435 			// LAMESPEC: MSDN says it should return null if the entry does not exist yet.
436 			//
437 			Assert.IsNotNull (ci2, "#A2-1");
438 			Assert.AreNotEqual (ci, ci2, "#A2-2");
439 			Assert.IsNull (ci2.Value, "#A2-3");
440 			Assert.IsTrue (mc.Contains (ci.Key), "#A2-4");
441 			Assert.AreEqual (ci.Key, ci2.Key, "#A2-5");
442 
443 			ci = new CacheItem ("key", "value");
444 			ci2 = mc.AddOrGetExisting (ci, null);
445 			Assert.IsNotNull (ci2, "#A3-1");
446 			Assert.AreNotEqual (ci, ci2, "#A3-2");
447 			Assert.IsNotNull (ci2.Value, "#A3-3");
448 			Assert.AreEqual (ci.Value, ci2.Value, "#A3-4");
449 			Assert.AreEqual (ci.Key, ci2.Key, "#A3-5");
450 
451 			Assert.Throws<ArgumentNullException> (() => {
452 				ci = new CacheItem (null, "value");
453 				ci2 = mc.AddOrGetExisting (ci, null);
454 			}, "#A4");
455 
456 			ci = new CacheItem (String.Empty, "value");
457 			ci2 = mc.AddOrGetExisting (ci, null);
458 			Assert.IsNotNull (ci2, "#A5-1");
459 			Assert.AreNotEqual (ci, ci2, "#A5-2");
460 			Assert.IsNull (ci2.Value, "#A5-3");
461 			Assert.IsTrue (mc.Contains (ci.Key), "#A5-4");
462 			Assert.AreEqual (ci.Key, ci2.Key, "#A5-5");
463 
464 			ci = new CacheItem ("key2", null);
465 
466 			// Thrown from:
467 			// at System.Runtime.Caching.MemoryCacheEntry..ctor(String key, Object value, DateTimeOffset absExp, TimeSpan slidingExp, CacheItemPriority priority, Collection`1 dependencies, CacheEntryRemovedCallback removedCallback, MemoryCache cache)
468 			// at System.Runtime.Caching.MemoryCache.AddOrGetExistingInternal(String key, Object value, CacheItemPolicy policy)
469 			// at System.Runtime.Caching.MemoryCache.AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
470 			// at MonoTests.System.Runtime.Caching.MemoryCacheTest.AddOrGetExisting_CacheItem_CacheItemPolicy() in C:\Users\grendel\documents\visual studio 2010\Projects\System.Runtime.Caching.Test\System.Runtime.Caching.Test\System.Runtime.Caching\MemoryCacheTest.cs:line 211
471 			Assert.Throws<ArgumentNullException> (() => {
472 				ci2 = mc.AddOrGetExisting (ci, null);
473 			}, "#B1");
474 
475 			ci = new CacheItem ("key3", "value");
476 			var cip = new CacheItemPolicy ();
477 			cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
478 			Assert.Throws<ArgumentException> (() => {
479 				ci2 = mc.AddOrGetExisting (ci, cip);
480 			}, "#B2");
481 
482 			ci = new CacheItem ("key3", "value");
483 			cip = new CacheItemPolicy ();
484 			cip.AbsoluteExpiration = DateTimeOffset.Now;
485 			cip.SlidingExpiration = TimeSpan.FromTicks (DateTime.Now.Ticks);
486 			Assert.Throws<ArgumentException> (() => {
487 				mc.AddOrGetExisting (ci, cip);
488 			}, "#B3");
489 
490 			ci = new CacheItem ("key3", "value");
491 			cip = new CacheItemPolicy ();
492 			cip.SlidingExpiration = TimeSpan.MinValue;
493 			Assert.Throws<ArgumentOutOfRangeException> (() => {
494 				mc.AddOrGetExisting (ci, cip);
495 			}, "#B4-1");
496 
497 			ci = new CacheItem ("key4_#B4-2", "value");
498 			cip = new CacheItemPolicy ();
499 			cip.SlidingExpiration = TimeSpan.FromTicks (0L);
500 			mc.AddOrGetExisting (ci, cip);
501 			Assert.IsTrue (mc.Contains ("key4_#B4-2"), "#B4-2");
502 
503 			ci = new CacheItem ("key3", "value");
504 			cip = new CacheItemPolicy ();
505 			cip.SlidingExpiration = TimeSpan.FromDays (500);
506 			Assert.Throws<ArgumentOutOfRangeException> (() => {
507 				mc.AddOrGetExisting (ci, cip);
508 			}, "#B5-1");
509 
510 			ci = new CacheItem ("key5_#B5-2", "value");
511 			cip = new CacheItemPolicy ();
512 			cip.SlidingExpiration = TimeSpan.FromDays (365);
513 			mc.AddOrGetExisting (ci, cip);
514 			Assert.IsTrue (mc.Contains ("key5_#B5-2"), "#B5-2");
515 
516 			ci = new CacheItem ("key3", "value");
517 			cip = new CacheItemPolicy ();
518 			cip.Priority = (CacheItemPriority)20;
519 			Assert.Throws<ArgumentOutOfRangeException> (() => {
520 				mc.AddOrGetExisting (ci, cip);
521 			}, "#B6");
522 
523 			ci = new CacheItem ("key3_B7", "value");
524 			cip = new CacheItemPolicy ();
525 			cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
526 			ci2 = mc.AddOrGetExisting (ci, cip);
527 			Assert.IsTrue (mc.Contains ("key3_B7"), "#B7");
528 
529 			// LAMESPEC: MSDN says it should return null if the entry does not exist yet.
530 			//
531 			Assert.IsNotNull (ci2, "#C1-1");
532 			Assert.AreNotEqual (ci, ci2, "#C1-2");
533 			Assert.IsNull (ci2.Value, "#C1-3");
534 			Assert.IsTrue (mc.Contains (ci.Key), "#C1-4");
535 			Assert.AreEqual (ci.Key, ci2.Key, "#C1-5");
536 
537 			// The entry is never inserted as its expiration date is before now
538 			ci = new CacheItem ("key_D1", "value_D1");
539 			cip = new CacheItemPolicy ();
540 			cip.AbsoluteExpiration = DateTimeOffset.MinValue;
541 			ci2 = mc.AddOrGetExisting (ci, cip);
542 			Assert.IsFalse (mc.Contains ("key_D1"), "#D1-1");
543 			Assert.IsNotNull (ci2, "#D1-2");
544 			Assert.IsNull (ci2.Value, "#D1-3");
545 			Assert.AreEqual ("key_D1", ci2.Key, "#D1-4");
546 
547 			mc.Calls.Clear ();
548 			ci = new CacheItem ("key_D2", "value_D2");
549 			cip = new CacheItemPolicy ();
550 			cip.AbsoluteExpiration = DateTimeOffset.MaxValue;
551 			mc.AddOrGetExisting (ci, cip);
552 			Assert.IsTrue (mc.Contains ("key_D2"), "#D2-1");
553 			Assert.AreEqual (2, mc.Calls.Count, "#D2-2");
554 			Assert.AreEqual ("AddOrGetExisting (CacheItem item, CacheItemPolicy policy)", mc.Calls [0], "#D2-3");
555 		}
556 
557 		[Test]
Set_String_Object_CacheItemPolicy_String()558 		public void Set_String_Object_CacheItemPolicy_String ()
559 		{
560 			var mc = new PokerMemoryCache ("MyCache");
561 
562 			Assert.Throws<NotSupportedException> (() => {
563 				mc.Set ("key", "value", new CacheItemPolicy (), "region");
564 			}, "#A1-1");
565 
566 			Assert.Throws<ArgumentNullException> (() => {
567 				mc.Set (null, "value", new CacheItemPolicy ());
568 			}, "#A1-2");
569 
570 			Assert.Throws<ArgumentNullException> (() => {
571 				mc.Set ("key", null, new CacheItemPolicy ());
572 			}, "#A1-3");
573 
574 			var cip = new CacheItemPolicy ();
575 			cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
576 			cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
577 			Assert.Throws<ArgumentException> (() => {
578 				mc.Set ("key", "value", cip);
579 			}, "#A1-4");
580 
581 			cip = new CacheItemPolicy ();
582 			cip.SlidingExpiration = TimeSpan.MinValue;
583 			Assert.Throws<ArgumentOutOfRangeException> (() => {
584 				mc.Set ("key", "value", cip);
585 			}, "#A1-5");
586 
587 			cip = new CacheItemPolicy ();
588 			cip.SlidingExpiration = TimeSpan.FromTicks (0L);
589 			mc.Set ("key_A1-6", "value", cip);
590 			Assert.IsTrue (mc.Contains ("key_A1-6"), "#A1-6");
591 
592 			cip = new CacheItemPolicy ();
593 			cip.SlidingExpiration = TimeSpan.FromDays (500);
594 			Assert.Throws<ArgumentOutOfRangeException> (() => {
595 				mc.Set ("key", "value", cip);
596 			}, "#A1-7");
597 
598 			cip = new CacheItemPolicy ();
599 			cip.SlidingExpiration = TimeSpan.FromDays (365);
600 			mc.Set ("key_A1-8", "value", cip);
601 			Assert.IsTrue (mc.Contains ("key_A1-8"), "#A1-8");
602 
603 			cip = new CacheItemPolicy ();
604 			cip.Priority = (CacheItemPriority) 20;
605 			Assert.Throws<ArgumentOutOfRangeException> (() => {
606 				mc.Set ("key", "value", cip);
607 			}, "#A1-9");
608 
609 			cip = new CacheItemPolicy ();
610 			cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
611 			mc.Set ("key_A2", "value_A2", cip);
612 			Assert.IsTrue (mc.Contains ("key_A2"), "#A2");
613 
614 			mc.Set ("key_A3", "value_A3", new CacheItemPolicy ());
615 			Assert.IsTrue (mc.Contains ("key_A3"), "#A3-1");
616 			Assert.AreEqual ("value_A3", mc.Get ("key_A3"), "#A3-2");
617 
618 			// The entry is never inserted as its expiration date is before now
619 			cip = new CacheItemPolicy ();
620 			cip.AbsoluteExpiration = DateTimeOffset.MinValue;
621 			mc.Set ("key_A4", "value_A4", cip);
622 			Assert.IsFalse (mc.Contains ("key_A4"), "#A4");
623 
624 			mc.Calls.Clear ();
625 			cip = new CacheItemPolicy ();
626 			cip.AbsoluteExpiration = DateTimeOffset.MaxValue;
627 			mc.Set ("key_A5", "value_A5", cip);
628 			Assert.IsTrue (mc.Contains ("key_A5"), "#A5-1");
629 			Assert.AreEqual (2, mc.Calls.Count, "#A5-2");
630 			Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [0], "#A5-3");
631 		}
632 
633 		[Test]
Set_String_Object_DateTimeOffset_String()634 		public void Set_String_Object_DateTimeOffset_String ()
635 		{
636 			var mc = new PokerMemoryCache ("MyCache");
637 
638 			Assert.Throws<NotSupportedException> (() => {
639 				mc.Set ("key", "value", DateTimeOffset.MaxValue, "region");
640 			}, "#A1-1");
641 
642 			Assert.Throws<ArgumentNullException> (() => {
643 				mc.Set (null, "value", DateTimeOffset.MaxValue);
644 			}, "#A1-2");
645 
646 			Assert.Throws<ArgumentNullException> (() => {
647 				mc.Set ("key", null, DateTimeOffset.MaxValue);
648 			}, "#A1-3");
649 
650 			// The entry is never inserted as its expiration date is before now
651 			mc.Set ("key_A2", "value_A2", DateTimeOffset.MinValue);
652 			Assert.IsFalse (mc.Contains ("key_A2"), "#A2");
653 
654 			mc.Calls.Clear ();
655 			mc.Set ("key", "value", DateTimeOffset.MaxValue);
656 
657 			Assert.AreEqual (2, mc.Calls.Count, "#A2-1");
658 			Assert.AreEqual ("Set (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls [0], "#A2-2");
659 			Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [1], "#A2-3");
660 		}
661 
662 		[Test]
Set_CacheItem_CacheItemPolicy()663 		public void Set_CacheItem_CacheItemPolicy ()
664 		{
665 			var mc = new PokerMemoryCache ("MyCache");
666 
667 			Assert.Throws<ArgumentNullException> (() => {
668 				mc.Set (null, new CacheItemPolicy ());
669 			}, "#A1-1");
670 
671 			// Actually thrown from the Set (string, object, CacheItemPolicy, string) overload
672 			var ci = new CacheItem (null, "value");
673 			Assert.Throws<ArgumentNullException> (() => {
674 				mc.Set (ci, new CacheItemPolicy ());
675 			}, "#A1-2");
676 
677 			ci = new CacheItem ("key", null);
678 			Assert.Throws<ArgumentNullException> (() => {
679 				mc.Set (ci, new CacheItemPolicy ());
680 			}, "#A1-3");
681 
682 			ci = new CacheItem ("key", "value");
683 			var cip = new CacheItemPolicy ();
684 			cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
685 			cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
686 			Assert.Throws<ArgumentException> (() => {
687 				mc.Set (ci, cip);
688 			}, "#A1-4");
689 
690 			ci = new CacheItem ("key", "value");
691 			cip = new CacheItemPolicy ();
692 			cip.SlidingExpiration = TimeSpan.MinValue;
693 			Assert.Throws<ArgumentOutOfRangeException> (() => {
694 				mc.Set (ci, cip);
695 			}, "#A1-5");
696 
697 			ci = new CacheItem ("key_A1-6", "value");
698 			cip = new CacheItemPolicy ();
699 			cip.SlidingExpiration = TimeSpan.FromTicks (0L);
700 			mc.Set (ci, cip);
701 			Assert.IsTrue (mc.Contains ("key_A1-6"), "#A1-6");
702 
703 			ci = new CacheItem ("key", "value");
704 			cip = new CacheItemPolicy ();
705 			cip.SlidingExpiration = TimeSpan.FromDays (500);
706 			Assert.Throws<ArgumentOutOfRangeException> (() => {
707 				mc.Set (ci, cip);
708 			}, "#A1-7");
709 
710 			ci = new CacheItem ("key_A1-8", "value");
711 			cip = new CacheItemPolicy ();
712 			cip.SlidingExpiration = TimeSpan.FromDays (365);
713 			mc.Set (ci, cip);
714 			Assert.IsTrue (mc.Contains ("key_A1-8"), "#A1-8");
715 
716 			ci = new CacheItem ("key", "value");
717 			cip = new CacheItemPolicy ();
718 			cip.Priority = (CacheItemPriority) 20;
719 			Assert.Throws<ArgumentOutOfRangeException> (() => {
720 				mc.Set (ci, cip);
721 			}, "#A1-9");
722 
723 			ci = new CacheItem ("key_A2", "value_A2");
724 			cip = new CacheItemPolicy ();
725 			cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
726 			mc.Set (ci, cip);
727 			Assert.IsTrue (mc.Contains ("key_A2"), "#A2");
728 
729 			ci = new CacheItem ("key_A3", "value_A3");
730 			mc.Set (ci, new CacheItemPolicy ());
731 			Assert.IsTrue (mc.Contains ("key_A3"), "#A3-1");
732 			Assert.AreEqual ("value_A3", mc.Get ("key_A3"), "#A3-2");
733 
734 			// The entry is never inserted as its expiration date is before now
735 			ci = new CacheItem ("key_A4", "value");
736 			cip = new CacheItemPolicy ();
737 			cip.AbsoluteExpiration = DateTimeOffset.MinValue;
738 			mc.Set (ci, cip);
739 			Assert.IsFalse (mc.Contains ("key_A4"), "#A4");
740 
741 			ci = new CacheItem ("key_A5", "value");
742 			mc.Calls.Clear ();
743 			mc.Set (ci, new CacheItemPolicy ());
744 
745 			Assert.AreEqual (2, mc.Calls.Count, "#A5-1");
746 			Assert.AreEqual ("Set (CacheItem item, CacheItemPolicy policy)", mc.Calls [0], "#A5-2");
747 			Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [1], "#A5-3");
748 		}
749 
750 		[Test]
Remove()751 		public void Remove ()
752 		{
753 			var mc = new PokerMemoryCache ("MyCache");
754 
755 			Assert.Throws<NotSupportedException> (() => {
756 				mc.Remove ("key", "region");
757 			}, "#A1-1");
758 
759 			Assert.Throws<ArgumentNullException> (() => {
760 				mc.Remove (null);
761 			}, "#A1-2");
762 
763 			bool callbackInvoked;
764 			CacheEntryRemovedReason reason = (CacheEntryRemovedReason) 1000;
765 			var cip = new CacheItemPolicy ();
766 			cip.Priority = CacheItemPriority.NotRemovable;
767 			mc.Set ("key2", "value1", cip);
768 			object value = mc.Remove ("key2");
769 
770 			Assert.IsNotNull (value, "#B1-1");
771 			Assert.IsFalse (mc.Contains ("key2"), "#B1-2");
772 
773 			cip = new CacheItemPolicy ();
774 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
775 				callbackInvoked = true;
776 				reason = args.RemovedReason;
777 			};
778 
779 			mc.Set ("key", "value", cip);
780 			callbackInvoked = false;
781 			reason = (CacheEntryRemovedReason) 1000;
782 			value = mc.Remove ("key");
783 			Assert.IsNotNull (value, "#C1-1");
784 			Assert.IsTrue (callbackInvoked, "#C1-2");
785 			Assert.AreEqual (CacheEntryRemovedReason.Removed, reason, "#C1-3");
786 
787 			cip = new CacheItemPolicy ();
788 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
789 				callbackInvoked = true;
790 				reason = args.RemovedReason;
791 				throw new ApplicationException ("test");
792 			};
793 
794 			mc.Set ("key", "value", cip);
795 			callbackInvoked = false;
796 			reason = (CacheEntryRemovedReason) 1000;
797 			value = mc.Remove ("key");
798 			Assert.IsNotNull (value, "#C2-1");
799 			Assert.IsTrue (callbackInvoked, "#C2-2");
800 			Assert.AreEqual (CacheEntryRemovedReason.Removed, reason, "#C2-3");
801 
802 			// LAMESPEC: UpdateCallback is not called on remove
803 			cip = new CacheItemPolicy ();
804 			cip.UpdateCallback = (CacheEntryUpdateArguments args) => {
805 				callbackInvoked = true;
806 				reason = args.RemovedReason;
807 			};
808 
809 			mc.Set ("key", "value", cip);
810 			callbackInvoked = false;
811 			reason = (CacheEntryRemovedReason) 1000;
812 			value = mc.Remove ("key");
813 			Assert.IsNotNull (value, "#D1-1");
814 			Assert.IsFalse (callbackInvoked, "#D1-2");
815 
816 			cip = new CacheItemPolicy ();
817 			cip.UpdateCallback = (CacheEntryUpdateArguments args) => {
818 				callbackInvoked = true;
819 				reason = args.RemovedReason;
820 				throw new ApplicationException ("test");
821 			};
822 
823 			mc.Set ("key", "value", cip);
824 			callbackInvoked = false;
825 			reason = (CacheEntryRemovedReason) 1000;
826 			value = mc.Remove ("key");
827 			Assert.IsNotNull (value, "#D2-1");
828 			Assert.IsFalse (callbackInvoked, "#D2-2");
829 		}
830 
831 		[Test]
TimedExpiration()832 		public void TimedExpiration ()
833 		{
834 			bool expired = false;
835 			CacheEntryRemovedReason reason = CacheEntryRemovedReason.CacheSpecificEviction;
836 			int sleepPeriod = 1100;
837 
838 			var mc = new PokerMemoryCache ("MyCache");
839 			var cip = new CacheItemPolicy ();
840 
841 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
842 				expired = true;
843 				reason = args.RemovedReason;
844 			};
845 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
846 			mc.Set ("key", "value", cip);
847 			Thread.Sleep (500);
848 
849 			Assert.IsFalse (expired, "#A1");
850 			object value = mc.Get ("key");
851 
852 			Assert.IsNull (value, "#A2-1");
853 			Assert.IsTrue (expired, "#A2-2");
854 			Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "A2-3");
855 
856 			expired = false;
857 			cip = new CacheItemPolicy ();
858 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
859 				expired = true;
860 				reason = args.RemovedReason;
861 			};
862 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
863 			mc.Set ("key", "value", cip);
864 			Thread.Sleep (sleepPeriod);
865 
866 			Assert.IsNull (mc.Get ("key"), "#A3-0");
867 			Assert.IsTrue (expired, "#A3-1");
868 			Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#A3-2");
869 
870 			int expiredCount = 0;
871 			object expiredCountLock = new object ();
872 			CacheEntryRemovedCallback removedCb = (CacheEntryRemovedArguments args) => {
873 				lock (expiredCountLock) {
874 					expiredCount++;
875 				}
876 			};
877 
878 			cip = new CacheItemPolicy ();
879 			cip.RemovedCallback = removedCb;
880 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (20);
881 			mc.Set ("key1", "value1", cip);
882 
883 			cip = new CacheItemPolicy ();
884 			cip.RemovedCallback = removedCb;
885 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (200);
886 			mc.Set ("key2", "value2", cip);
887 
888 			cip = new CacheItemPolicy ();
889 			cip.RemovedCallback = removedCb;
890 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (600);
891 			mc.Set ("key3", "value3", cip);
892 
893 			cip = new CacheItemPolicy ();
894 			cip.RemovedCallback = removedCb;
895 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (sleepPeriod + 500);
896 			mc.Set ("key4", "value4", cip);
897 
898 			Thread.Sleep (sleepPeriod);
899 			Assert.IsNull (mc.Get ("key1"), "#A4-1");
900 			Assert.IsNull (mc.Get ("key2"), "#A4-2");
901 			Assert.IsNull (mc.Get ("key3"), "#A4-3");
902 			Assert.IsNotNull (mc.Get ("key4"), "#A4-4");
903 			Assert.AreEqual (3, expiredCount, "#A4");
904 		}
905 
906 		[Test]
GetEnumerator()907 		public void GetEnumerator ()
908 		{
909 			var mc = new PokerMemoryCache ("MyCache");
910 
911 			// This one is a Hashtable enumerator
912 			IEnumerator enumerator = ((IEnumerable) mc).GetEnumerator ();
913 
914 			// This one is a Dictionary <string, object> enumerator
915 			IEnumerator enumerator2 = mc.DoGetEnumerator ();
916 
917 			Assert.IsNotNull (enumerator, "#A1-1");
918 			Assert.IsNotNull (enumerator2, "#A1-2");
919 			Assert.IsTrue (enumerator.GetType () != enumerator2.GetType (), "#A1-3");
920 
921 			mc.Set ("key1", "value1", null);
922 			mc.Set ("key2", "value2", null);
923 			mc.Set ("key3", "value3", null);
924 
925 			bool expired = false;
926 			var cip = new CacheItemPolicy ();
927 			cip.AbsoluteExpiration = DateTime.Now.AddMilliseconds (50);
928 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
929 				expired = true;
930 			};
931 
932 			mc.Set ("key4", "value4", cip);
933 			Thread.Sleep (500);
934 
935 			enumerator = ((IEnumerable) mc).GetEnumerator ();
936 			int count = 0;
937 			while (enumerator.MoveNext ()) {
938 				count++;
939 			}
940 
941 			Assert.IsFalse (expired, "#A2-1");
942 			Assert.AreEqual (3, count, "#A2-2");
943 
944 			expired = false;
945 			cip = new CacheItemPolicy ();
946 			cip.AbsoluteExpiration = DateTime.Now.AddMilliseconds (50);
947 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
948 				expired = true;
949 			};
950 
951 			mc.Set ("key5", "value5", cip);
952 			Thread.Sleep (500);
953 
954 			enumerator2 = mc.DoGetEnumerator ();
955 			count = 0;
956 			while (enumerator2.MoveNext ()) {
957 				count++;
958 			}
959 
960 			Assert.IsFalse (expired, "#A3-1");
961 			Assert.AreEqual (3, count, "#A3-2");
962 		}
963 
964 		[Test]
GetValues()965 		public void GetValues ()
966 		{
967 			var mc = new PokerMemoryCache ("MyCache");
968 
969 			Assert.Throws<ArgumentNullException> (() => {
970 				mc.GetValues ((string[]) null);
971 			}, "#A1-1");
972 
973 			Assert.Throws<NotSupportedException> (() => {
974 				mc.GetValues (new string[] {}, "region");
975 			}, "#A1-2");
976 
977 			Assert.Throws<ArgumentException> (() => {
978 				mc.GetValues (new string [] { "key", null });
979 			}, "#A1-3");
980 
981 			IDictionary<string, object> value = mc.GetValues (new string[] {});
982 			Assert.IsNull (value, "#A2");
983 
984 			mc.Set ("key1", "value1", null);
985 			mc.Set ("key2", "value2", null);
986 			mc.Set ("key3", "value3", null);
987 
988 			Assert.IsTrue (mc.Contains ("key1"), "#A3-1");
989 			Assert.IsTrue (mc.Contains ("key2"), "#A3-2");
990 			Assert.IsTrue (mc.Contains ("key3"), "#A3-2");
991 
992 			value = mc.GetValues (new string [] { "key1", "key3" });
993 			Assert.IsNotNull (value, "#A4-1");
994 			Assert.AreEqual (2, value.Count, "#A4-2");
995 			Assert.AreEqual ("value1", value ["key1"], "#A4-3");
996 			Assert.AreEqual ("value3", value ["key3"], "#A4-4");
997 			Assert.AreEqual (typeof (Dictionary<string, object>), value.GetType (), "#A4-5");
998 
999 			// LAMESPEC: MSDN says the number of items in the returned dictionary should be the same as in the
1000 			// 'keys' collection - this is not the case. The returned dictionary contains only entries for keys
1001 			// that exist in the cache.
1002 			value = mc.GetValues (new string [] { "key1", "key3", "nosuchkey" });
1003 			Assert.IsNotNull (value, "#A5-1");
1004 			Assert.AreEqual (2, value.Count, "#A5-2");
1005 			Assert.AreEqual ("value1", value ["key1"], "#A5-3");
1006 			Assert.AreEqual ("value3", value ["key3"], "#A5-4");
1007 			Assert.IsFalse (value.ContainsKey ("Key1"), "#A5-5");
1008 		}
1009 
1010 		[Test]
Get()1011 		public void Get ()
1012 		{
1013 			var mc = new PokerMemoryCache ("MyCache");
1014 
1015 			Assert.Throws<NotSupportedException> (() => {
1016 				mc.Get ("key", "region");
1017 			}, "#A1-1");
1018 
1019 			Assert.Throws<ArgumentNullException> (() => {
1020 				mc.Get (null);
1021 			}, "#A1-2");
1022 
1023 			object value;
1024 			mc.Set ("key", "value", null);
1025 			value = mc.Get ("key");
1026 			Assert.IsNotNull (value, "#A2-1");
1027 			Assert.AreEqual ("value", value, "#A2-2");
1028 
1029 			value = mc.Get ("nosuchkey");
1030 			Assert.IsNull (value, "#A3");
1031 
1032 			var cip = new CacheItemPolicy ();
1033 			bool callbackInvoked;
1034 			CacheEntryRemovedReason reason = (CacheEntryRemovedReason)1000;
1035 
1036 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1037 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1038 				callbackInvoked = true;
1039 				reason = args.RemovedReason;
1040 			};
1041 			mc.Set ("key", "value", cip);
1042 			Thread.Sleep (500);
1043 
1044 			callbackInvoked = false;
1045 			reason = (CacheEntryRemovedReason) 1000;
1046 			value = mc.Get ("key");
1047 			Assert.IsNull (value, "#B1-1");
1048 			Assert.IsTrue (callbackInvoked, "#B1-2");
1049 			Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B1-3");
1050 
1051 			cip = new CacheItemPolicy ();
1052 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1053 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1054 				callbackInvoked = true;
1055 				reason = args.RemovedReason;
1056 				throw new ApplicationException ("test");
1057 			};
1058 
1059 			mc.Set ("key", "value", cip);
1060 			Thread.Sleep (500);
1061 
1062 			callbackInvoked = false;
1063 			reason = (CacheEntryRemovedReason) 1000;
1064 			value = mc.Get ("key");
1065 			Assert.IsNull (value, "#B2-1");
1066 			Assert.IsTrue (callbackInvoked, "#B2-2");
1067 			Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B2-3");
1068 		}
1069 
1070 		[Test]
GetCacheItem()1071 		public void GetCacheItem ()
1072 		{
1073 			var mc = new PokerMemoryCache ("MyCache");
1074 
1075 			Assert.Throws<NotSupportedException> (() => {
1076 				mc.GetCacheItem ("key", "region");
1077 			}, "#A1-1");
1078 
1079 			Assert.Throws<ArgumentNullException> (() => {
1080 				mc.GetCacheItem (null);
1081 			}, "#A1-2");
1082 
1083 			CacheItem value;
1084 			mc.Set ("key", "value", null);
1085 			value = mc.GetCacheItem ("key");
1086 			Assert.IsNotNull (value, "#A2-1");
1087 			Assert.AreEqual ("value", value.Value, "#A2-2");
1088 			Assert.AreEqual ("key", value.Key, "#A2-3");
1089 
1090 			value = mc.GetCacheItem ("doesnotexist");
1091 			Assert.IsNull (value, "#A3");
1092 
1093 			var cip = new CacheItemPolicy ();
1094 			bool callbackInvoked;
1095 			CacheEntryRemovedReason reason = (CacheEntryRemovedReason) 1000;
1096 
1097 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1098 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1099 				callbackInvoked = true;
1100 				reason = args.RemovedReason;
1101 			};
1102 			mc.Set ("key", "value", cip);
1103 			Thread.Sleep (500);
1104 
1105 			callbackInvoked = false;
1106 			reason = (CacheEntryRemovedReason) 1000;
1107 			value = mc.GetCacheItem ("key");
1108 			Assert.IsNull (value, "#B1-1");
1109 			Assert.IsTrue (callbackInvoked, "#B1-2");
1110 			Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B1-3");
1111 
1112 			cip = new CacheItemPolicy ();
1113 			cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1114 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1115 				callbackInvoked = true;
1116 				reason = args.RemovedReason;
1117 				throw new ApplicationException ("test");
1118 			};
1119 
1120 			mc.Set ("key", "value", cip);
1121 			Thread.Sleep (500);
1122 
1123 			callbackInvoked = false;
1124 			reason = (CacheEntryRemovedReason) 1000;
1125 			value = mc.GetCacheItem ("key");
1126 			Assert.IsNull (value, "#B2-1");
1127 			Assert.IsTrue (callbackInvoked, "#B2-2");
1128 			Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B2-3");
1129 		}
1130 
1131 		[Test]
ChangeMonitors()1132 		public void ChangeMonitors ()
1133 		{
1134 			bool removed = false;
1135 			var mc = new PokerMemoryCache ("MyCache");
1136 			var cip = new CacheItemPolicy ();
1137 			var monitor = new PokerChangeMonitor ();
1138 			cip.ChangeMonitors.Add (monitor);
1139 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1140 				removed = true;
1141 			};
1142 
1143 			mc.Set ("key", "value", cip);
1144 			Assert.AreEqual (0, monitor.Calls.Count, "#A1");
1145 
1146 			monitor.SignalChange ();
1147 			Assert.IsTrue (removed, "#A2");
1148 
1149 			bool onChangedCalled = false;
1150 			monitor = new PokerChangeMonitor ();
1151 			monitor.NotifyOnChanged ((object state) => {
1152 				onChangedCalled = true;
1153 			});
1154 
1155 			cip = new CacheItemPolicy ();
1156 			cip.ChangeMonitors.Add (monitor);
1157 
1158 			// Thrown by ChangeMonitor.NotifyOnChanged
1159 			Assert.Throws<InvalidOperationException> (() => {
1160 				mc.Set ("key1", "value1", cip);
1161 			}, "#A3");
1162 		}
1163 
1164 		// NOTE: on Windows with 2 or more CPUs this test will most probably fail.
1165 		[Test]
Trim()1166 		public void Trim ()
1167 		{
1168 			var config = new NameValueCollection ();
1169 			config ["__MonoEmulateOneCPU"] = "true";
1170 			var mc = new MemoryCache ("MyCache", config);
1171 
1172 			for (int i = 0; i < 10; i++)
1173 				mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1174 
1175 			Assert.AreEqual (10, mc.GetCount (), "#A1-1");
1176 			long trimmed = mc.Trim (50);
1177 			Assert.AreEqual (5, trimmed, "#A1-2");
1178 			Assert.AreEqual (5, mc.GetCount (), "#A1-3");
1179 
1180 			mc = new MemoryCache ("MyCache", config);
1181 			// Only entries 11- are considered for removal
1182 			for (int i = 0; i < 11; i++)
1183 				mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1184 
1185 			Assert.AreEqual (11, mc.GetCount (), "#A2-1");
1186 			trimmed = mc.Trim (50);
1187 			Assert.AreEqual (6, trimmed, "#A2-2");
1188 			Assert.AreEqual (5, mc.GetCount (), "#A2-3");
1189 
1190 			mc = new MemoryCache ("MyCache", config);
1191 			// Only entries 11- are considered for removal
1192 			for (int i = 0; i < 125; i++)
1193 				mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1194 
1195 			Assert.AreEqual (125, mc.GetCount (), "#A3-1");
1196 			trimmed = mc.Trim (50);
1197 			Assert.AreEqual (63, trimmed, "#A3-2");
1198 			Assert.AreEqual (62, mc.GetCount (), "#A3-3");
1199 
1200 			// Testing the removal order
1201 			mc = new MemoryCache ("MyCache", config);
1202 			var removed = new List <string> ();
1203 			var cip = new CacheItemPolicy ();
1204 			cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1205 				removed.Add (args.CacheItem.Key);
1206 			};
1207 
1208 			for (int i = 0; i < 50; i++)
1209 				mc.Set ("key" + i.ToString (), "value" + i.ToString (), cip);
1210 
1211 			object value;
1212 			for (int i = 0; i < 50; i++)
1213 				value = mc.Get ("key" + i.ToString ());
1214 
1215 			trimmed = mc.Trim (50);
1216 			Assert.AreEqual (25, mc.GetCount (), "#A4-1");
1217 			Assert.AreEqual (25, trimmed, "#A4-2");
1218 			Assert.AreEqual (25, removed.Count, "#A4-3");
1219 
1220 			for (int i = 0; i < 25; i++)
1221 				Assert.AreEqual ("key" + i.ToString (), removed [i], "#A5-" + i.ToString ());
1222 		}
1223 
1224 		[Test]
TestCacheShrink()1225 		public void TestCacheShrink ()
1226 		{
1227 			const int HEAP_RESIZE_THRESHOLD = 8192 + 2;
1228 			const int HEAP_RESIZE_SHORT_ENTRIES = 2048;
1229 			const int HEAP_RESIZE_LONG_ENTRIES = HEAP_RESIZE_THRESHOLD - HEAP_RESIZE_SHORT_ENTRIES;
1230 
1231 			var config = new NameValueCollection ();
1232 			config["cacheMemoryLimitMegabytes"] = 0.ToString ();
1233 			config["physicalMemoryLimitPercentage"] = 100.ToString ();
1234 			config["pollingInterval"] = new TimeSpan (0, 0, 1).ToString ();
1235 
1236 			using (var mc = new MemoryCache ("TestCacheShrink",  config)) {
1237 				Assert.AreEqual (0, mc.GetCount (), "#CS1");
1238 
1239 				// add some short duration entries
1240 				for (int i = 0; i < HEAP_RESIZE_SHORT_ENTRIES; i++) {
1241 					var expireAt = DateTimeOffset.Now.AddSeconds (3);
1242 					mc.Add ("short-" + i, i.ToString (), expireAt);
1243 				}
1244 
1245 				Assert.AreEqual (HEAP_RESIZE_SHORT_ENTRIES, mc.GetCount (), "#CS2");
1246 
1247 				// add some long duration entries
1248 				for (int i = 0; i < HEAP_RESIZE_LONG_ENTRIES; i++) {
1249 					var expireAt = DateTimeOffset.Now.AddSeconds (12);
1250 					mc.Add ("long-" + i, i.ToString (), expireAt);
1251 				}
1252 
1253 				Assert.AreEqual (HEAP_RESIZE_LONG_ENTRIES + HEAP_RESIZE_SHORT_ENTRIES, mc.GetCount(), "#CS3");
1254 
1255 				// wait for the cache thread to expire the short duration items, this will also shrink the size of the cache
1256 				global::System.Threading.Thread.Sleep (5 * 1000);
1257 
1258 				for (int i = 0; i < HEAP_RESIZE_SHORT_ENTRIES; i++) {
1259 					Assert.IsNull (mc.Get ("short-" + i), "#CS4-" + i);
1260 				}
1261 				Assert.AreEqual (HEAP_RESIZE_LONG_ENTRIES, mc.GetCount (), "#CS4");
1262 
1263 				// add some new items into the cache, this will grow the cache again
1264 				for (int i = 0; i < HEAP_RESIZE_LONG_ENTRIES; i++) {
1265 					mc.Add("final-" + i, i.ToString (), DateTimeOffset.Now.AddSeconds (4));
1266 				}
1267 
1268 				Assert.AreEqual (HEAP_RESIZE_LONG_ENTRIES + HEAP_RESIZE_LONG_ENTRIES, mc.GetCount (), "#CS5");
1269 			}
1270 		}
1271 
1272 		[Test]
TestExpiredGetValues()1273 		public void TestExpiredGetValues ()
1274 		{
1275 			var config = new NameValueCollection ();
1276 			config["cacheMemoryLimitMegabytes"] = 0.ToString ();
1277 			config["physicalMemoryLimitPercentage"] = 100.ToString ();
1278 			config["pollingInterval"] = new TimeSpan (0, 0, 10).ToString ();
1279 
1280 			using (var mc = new MemoryCache ("TestExpiredGetValues",  config)) {
1281 				Assert.AreEqual (0, mc.GetCount (), "#EGV1");
1282 
1283 				var keys = new List<string> ();
1284 
1285 				// add some short duration entries
1286 				for (int i = 0; i < 10; i++) {
1287 					var key = "short-" + i;
1288 					var expireAt = DateTimeOffset.Now.AddSeconds (1);
1289 					mc.Add (key, i.ToString (), expireAt);
1290 
1291 					keys.Add (key);
1292 				}
1293 
1294 				Assert.AreEqual (10, mc.GetCount (), "#EGV2");
1295 
1296 				global::System.Threading.Thread.Sleep (4 * 1000);
1297 
1298 				// we have waited but the items won't be expired by the timer since it wont have fired yet
1299 				Assert.AreEqual (10, mc.GetCount (), "#EGV3");
1300 
1301 				// calling GetValues() will expire the items since we are now past their expiresAt
1302 				mc.GetValues (keys);
1303 
1304 				Assert.AreEqual (0, mc.GetCount (), "#EGV4");
1305 			}
1306 		}
1307 
1308 		[Test]
TestCacheExpiryOrdering()1309 		public void TestCacheExpiryOrdering ()
1310 		{
1311 			var config = new NameValueCollection ();
1312 			config["cacheMemoryLimitMegabytes"] = 0.ToString ();
1313 			config["physicalMemoryLimitPercentage"] = 100.ToString ();
1314 			config["pollingInterval"] = new TimeSpan (0, 0, 1).ToString ();
1315 
1316 			using (var mc = new MemoryCache ("TestCacheExpiryOrdering",  config)) {
1317 				Assert.AreEqual (0, mc.GetCount (), "#CEO1");
1318 
1319 				// add long lived items into the cache first
1320 				for (int i = 0; i < 100; i++) {
1321 					var cip = new CacheItemPolicy ();
1322 					cip.SlidingExpiration = new TimeSpan (0, 0, 10);
1323 					mc.Add ("long-" + i, i, cip);
1324 				}
1325 
1326 				Assert.AreEqual (100, mc.GetCount (), "#CEO2");
1327 
1328 				// add shorter lived items into the cache, these should expire first
1329 				for (int i = 0; i < 100; i++) {
1330 					var cip = new CacheItemPolicy ();
1331 					cip.SlidingExpiration = new TimeSpan(0, 0, 1);
1332 					mc.Add ("short-" + i, i, cip);
1333 				}
1334 
1335 				Assert.AreEqual (200, mc.GetCount (), "#CEO3");
1336 
1337 				global::System.Threading.Thread.Sleep (4 * 1000);
1338 
1339 				for (int i = 0; i < 100; i++) {
1340 					Assert.IsNull (mc.Get ("short-" + i), "#CEO4-" + i);
1341 				}
1342 				Assert.AreEqual (100, mc.GetCount (), "#CEO4");
1343 			}
1344 		}
1345 
1346 		[Test]
TestCacheSliding()1347 		public void TestCacheSliding ()
1348 		{
1349 			var config = new NameValueCollection ();
1350 			config["cacheMemoryLimitMegabytes"] = 0.ToString ();
1351 			config["physicalMemoryLimitPercentage"] = 100.ToString ();
1352 			config["pollingInterval"] = new TimeSpan (0, 0, 1).ToString ();
1353 
1354 			using (var mc = new MemoryCache ("TestCacheSliding",  config)) {
1355 				Assert.AreEqual (0, mc.GetCount (), "#CSL1");
1356 
1357 				var cip = new CacheItemPolicy();
1358 				// The sliding expiration timeout has to be greater than 1 second because
1359 				// .NET implementation ignores timeouts updates smaller than
1360 				// CacheExpires.MIN_UPDATE_DELTA which is equal to 1.
1361 				cip.SlidingExpiration = new TimeSpan (0, 0, 2);
1362 				mc.Add("slidingtest", "42", cip);
1363 
1364 				mc.Add("expire1", "1", cip);
1365 				mc.Add("expire2", "2", cip);
1366 				mc.Add("expire3", "3", cip);
1367 				mc.Add("expire4", "4", cip);
1368 				mc.Add("expire5", "5", cip);
1369 
1370 				Assert.AreEqual (6, mc.GetCount (), "#CSL2");
1371 
1372 				for (int i = 0; i < 50; i++) {
1373 					global::System.Threading.Thread.Sleep (100);
1374 
1375 					var item = mc.Get ("slidingtest");
1376 					Assert.AreNotEqual (null, item, "#CSL3-" + i);
1377 				}
1378 
1379 				Assert.IsNull (mc.Get ("expire1"), "#CSL4-1");
1380 				Assert.IsNull (mc.Get ("expire2"), "#CSL4-2");
1381 				Assert.IsNull (mc.Get ("expire3"), "#CSL4-3");
1382 				Assert.IsNull (mc.Get ("expire4"), "#CSL4-4");
1383 				Assert.IsNull (mc.Get ("expire5"), "#CSL4-5");
1384 				Assert.AreEqual (1, mc.GetCount (), "#CSL4");
1385 
1386 				global::System.Threading.Thread.Sleep (4 * 1000);
1387 
1388 				Assert.IsNull (mc.Get ("slidingtest"), "#CSL5a");
1389 				Assert.AreEqual (0, mc.GetCount (), "#CSL5");
1390 			}
1391 		}
1392 	}
1393 }
1394