Merge remote branch 'upstream/master'
[mono.git] / mcs / class / System.Runtime.Caching / Test / System.Runtime.Caching / MemoryCacheTest.cs
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]
45                 public void ConstructorParameters ()
46                 {
47                         MemoryCache mc;
48                         AssertExtensions.Throws<ArgumentNullException> (() => {
49                                 mc = new MemoryCache (null);
50                         }, "#A1");
51
52                         AssertExtensions.Throws<ArgumentException> (() => {
53                                 mc = new MemoryCache (String.Empty);
54                         }, "#A2");
55
56                         AssertExtensions.Throws<ArgumentException> (() => {
57                                 mc = new MemoryCache ("default");
58                         }, "#A3");
59
60                         var config = new NameValueCollection ();
61                         config.Add ("CacheMemoryLimitMegabytes", "invalid");
62                         AssertExtensions.Throws<ArgumentException> (() => {
63                                 mc = new MemoryCache ("MyCache", config);
64                         }, "#A4-1");
65
66                         config.Clear ();
67                         config.Add ("PhysicalMemoryLimitPercentage", "invalid");
68                         AssertExtensions.Throws<ArgumentException> (() => {
69                                 mc = new MemoryCache ("MyCache", config);
70                         }, "#A4-2");
71
72                         config.Clear ();
73                         config.Add ("PollingInterval", "invalid");
74                         AssertExtensions.Throws<ArgumentException> (() => {
75                                 mc = new MemoryCache ("MyCache", config);
76                         }, "#A4-3");
77
78                         config.Clear ();
79                         config.Add ("CacheMemoryLimitMegabytes", "-1");
80                         AssertExtensions.Throws<ArgumentException> (() => {
81                                 mc = new MemoryCache ("MyCache", config);
82                         }, "#A4-4");
83
84                         config.Clear ();
85                         config.Add ("CacheMemoryLimitMegabytes", UInt64.MaxValue.ToString ());
86                         AssertExtensions.Throws<ArgumentException> (() => {
87                                 mc = new MemoryCache ("MyCache", config);
88                         }, "#A4-5");
89
90                         config.Clear ();
91                         config.Add ("PhysicalMemoryLimitPercentage", "-1");
92                         AssertExtensions.Throws<ArgumentException> (() => {
93                                 mc = new MemoryCache ("MyCache", config);
94                         }, "#A4-6");
95
96                         config.Clear ();
97                         config.Add ("PhysicalMemoryLimitPercentage", UInt64.MaxValue.ToString ());
98                         AssertExtensions.Throws<ArgumentException> (() => {
99                                 mc = new MemoryCache ("MyCache", config);
100                         }, "#A4-7");
101
102                         config.Clear ();
103                         config.Add ("PhysicalMemoryLimitPercentage", UInt32.MaxValue.ToString ());
104                         AssertExtensions.Throws<ArgumentException> (() => {
105                                 mc = new MemoryCache ("MyCache", config);
106                         }, "#A4-8");
107
108                         config.Clear ();
109                         config.Add ("PhysicalMemoryLimitPercentage", "-10");
110                         AssertExtensions.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                         AssertExtensions.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]
132                 public void Defaults ()
133                 {
134                         var mc = new MemoryCache ("MyCache");
135                         Assert.AreEqual ("MyCache", mc.Name, "#A1");
136                         Assert.AreEqual (98, mc.PhysicalMemoryLimit, "#A2");
137                         // Value of this property is different from system to system
138                         //Assert.AreEqual (0, mc.CacheMemoryLimit, "#A3");
139                         Assert.AreEqual (TimeSpan.FromMinutes (2), mc.PollingInterval, "#A4");
140                         Assert.AreEqual (
141                                 DefaultCacheCapabilities.InMemoryProvider |
142                                 DefaultCacheCapabilities.CacheEntryChangeMonitors |
143                                 DefaultCacheCapabilities.AbsoluteExpirations |
144                                 DefaultCacheCapabilities.SlidingExpirations |
145                                 DefaultCacheCapabilities.CacheEntryRemovedCallback |
146                                 DefaultCacheCapabilities.CacheEntryUpdateCallback,
147                                 mc.DefaultCacheCapabilities, "#A1");
148                 }
149
150                 [Test]
151                 public void DefaultInstanceDefaults ()
152                 {
153                         var mc = MemoryCache.Default;
154                         Assert.AreEqual ("Default", mc.Name, "#A1");
155                         Assert.AreEqual (98, mc.PhysicalMemoryLimit, "#A2");
156                         // Value of this property is different from system to system
157                         //Assert.AreEqual (0, mc.CacheMemoryLimit, "#A3");
158                         Assert.AreEqual (TimeSpan.FromMinutes (2), mc.PollingInterval, "#A4");
159                         Assert.AreEqual (
160                                 DefaultCacheCapabilities.InMemoryProvider |
161                                 DefaultCacheCapabilities.CacheEntryChangeMonitors |
162                                 DefaultCacheCapabilities.AbsoluteExpirations |
163                                 DefaultCacheCapabilities.SlidingExpirations |
164                                 DefaultCacheCapabilities.CacheEntryRemovedCallback |
165                                 DefaultCacheCapabilities.CacheEntryUpdateCallback,
166                                 mc.DefaultCacheCapabilities, "#A1");
167                 }
168
169                 [Test]
170                 public void ConstructorValues ()
171                 {
172                         var config = new NameValueCollection ();
173                         config.Add ("PhysicalMemoryLimitPercentage", "0");
174                         config.Add ("CacheMemoryLimitMegabytes", "1");
175                         config.Add ("pollingInterval", "00:10:00");
176
177                         var mc = new MemoryCache ("MyCache", config);
178                         Assert.AreEqual (98, mc.PhysicalMemoryLimit, "#A1");
179                         Assert.AreEqual (1048576, mc.CacheMemoryLimit, "#A2");
180                         Assert.AreEqual (TimeSpan.FromMinutes (10), mc.PollingInterval, "#A3");
181
182                         config.Clear ();
183                         config.Add ("PhysicalMemoryLimitPercentage", "10");
184                         config.Add ("CacheMemoryLimitMegabytes", "5");
185                         config.Add ("PollingInterval", "01:10:00");
186
187                         mc = new MemoryCache ("MyCache", config);
188                         Assert.AreEqual (10, mc.PhysicalMemoryLimit, "#B1");
189                         Assert.AreEqual (5242880, mc.CacheMemoryLimit, "#B2");
190                         Assert.AreEqual (TimeSpan.FromMinutes (70), mc.PollingInterval, "#B3");
191                 }
192
193                 [Test]
194                 public void Indexer ()
195                 {
196                         var mc = new PokerMemoryCache ("MyCache");
197
198                         AssertExtensions.Throws<ArgumentNullException> (() => {
199                                 mc [null] = "value";
200                         }, "#A1-1");
201
202                         AssertExtensions.Throws<ArgumentNullException> (() => {
203                                 object v = mc [null];
204                         }, "#A1-2");
205
206                         AssertExtensions.Throws<ArgumentNullException> (() => {
207                                 mc ["key"] = null;
208                         }, "#A1-3");
209
210                         mc.Calls.Clear ();
211                         mc ["key"] = "value";
212                         Assert.AreEqual (3, mc.Calls.Count, "#A2-1");
213                         Assert.AreEqual ("set_this [string key]", mc.Calls [0], "#A2-2");
214                         Assert.AreEqual ("Set (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls [1], "#A2-3");
215                         Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [2], "#A2-4");
216                         Assert.IsTrue (mc.Contains ("key"), "#A2-5");
217
218                         mc.Calls.Clear ();
219                         object value = mc ["key"];
220                         Assert.AreEqual (1, mc.Calls.Count, "#A3-1");
221                         Assert.AreEqual ("get_this [string key]", mc.Calls [0], "#A3-2");
222                         Assert.AreEqual ("value", value, "#A3-3");
223                 }
224
225                 [Test]
226                 public void Contains ()
227                 {
228                         var mc = new PokerMemoryCache ("MyCache");
229
230                         AssertExtensions.Throws<ArgumentNullException> (() => {
231                                 mc.Contains (null);
232                         }, "#A1-1");
233
234                         AssertExtensions.Throws<NotSupportedException> (() => {
235                                 mc.Contains ("key", "region");
236                         }, "#A1-2");
237
238                         mc.Set ("key", "value", ObjectCache.InfiniteAbsoluteExpiration);
239                         Assert.IsTrue (mc.Contains ("key"), "#A2");
240
241                         var cip = new CacheItemPolicy ();
242                         cip.Priority = CacheItemPriority.NotRemovable;
243                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (500);
244                         mc.Set ("key", "value", cip);
245                         Assert.IsTrue (mc.Contains ("key"), "#B1-1");
246                         Thread.Sleep (1000);
247                         // The call below removes the expired entry and returns false
248                         Assert.IsFalse (mc.Contains ("key"), "#B1-2");
249                 }
250
251                 [Test]
252                 public void CreateCacheEntryChangeMonitor ()
253                 {
254                         var mc = new PokerMemoryCache ("MyCache");
255
256                         AssertExtensions.Throws<NotSupportedException> (() => {
257                                 mc.CreateCacheEntryChangeMonitor (new string [] { "key" }, "region");
258                         }, "#A1-1");
259
260                         AssertExtensions.Throws<ArgumentNullException> (() => {
261                                 mc.CreateCacheEntryChangeMonitor (null);
262                         }, "#A1-2");
263
264                         AssertExtensions.Throws<ArgumentException> (() => {
265                                 mc.CreateCacheEntryChangeMonitor (new string [] {});
266                         }, "#A1-3");
267
268                         AssertExtensions.Throws<ArgumentException> (() => {
269                                 mc.CreateCacheEntryChangeMonitor (new string [] { "key", null });
270                         }, "#A1-4");
271
272                         mc.Set ("key1", "value1", ObjectCache.InfiniteAbsoluteExpiration);
273                         mc.Set ("key2", "value2", ObjectCache.InfiniteAbsoluteExpiration);
274                         mc.Set ("key3", "value3", ObjectCache.InfiniteAbsoluteExpiration);
275
276                         CacheEntryChangeMonitor monitor = mc.CreateCacheEntryChangeMonitor (new string [] { "key1", "key2" });
277                         Assert.IsNotNull (monitor, "#A2-1");
278                         Assert.AreEqual ("System.Runtime.Caching.MemoryCacheEntryChangeMonitor", monitor.GetType ().ToString (), "#A2-2");
279                         Assert.AreEqual (2, monitor.CacheKeys.Count, "#A2-3");
280                         Assert.AreEqual ("key1", monitor.CacheKeys [0], "#A2-3-1");
281                         Assert.AreEqual ("key2", monitor.CacheKeys [1], "#A2-3-2");
282                         Assert.IsNull (monitor.RegionName, "#A2-4");
283                         // Since this comparison can fail from time to time, leaving it commented out
284                         //Assert.AreEqual (DateTimeOffset.UtcNow.ToString (), monitor.LastModified.ToString (), "#A2-5");
285                         Assert.IsFalse (monitor.HasChanged, "#A2-5");
286
287                         // The actual unique id is constructed from key names followed by the hex value of ticks of their last modifed time
288                         Assert.IsFalse (String.IsNullOrEmpty (monitor.UniqueId), "#A2-6");
289
290                         // There seems to be a bug in .NET 4.0 regarding the code below. MSDN says that non-existing keys will cause the
291                         // returned monitor instance to be marked as changed, but instead this exception is thrown:
292                         //
293                         // MonoTests.System.Runtime.Caching.MemoryCacheTest.CreateCacheEntryChangeMonitor:
294                         // System.ArgumentOutOfRangeException : The UTC time represented when the offset is applied must be between year 0 and 10,000.
295                         // Parameter name: offset
296                         // 
297                         // at System.DateTimeOffset.ValidateDate(DateTime dateTime, TimeSpan offset)
298                         // at System.DateTimeOffset..ctor(DateTime dateTime)
299                         // at System.Runtime.Caching.MemoryCacheEntryChangeMonitor.InitDisposableMembers(MemoryCache cache)
300                         // at System.Runtime.Caching.MemoryCache.CreateCacheEntryChangeMonitor(IEnumerable`1 keys, String regionName)
301                         // 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
302                         // 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
303                         //
304                         // It's probably caused by the code passing a DateTime.MinValue to DateTimeOffset constructor for non-existing entries.
305                         // Until this (apparent) bug is fixed, Mono is going to implement the buggy behavior.
306                         //
307 #if false
308                         monitor = mc.CreateCacheEntryChangeMonitor (new string [] { "key1", "doesnotexist" });
309                         Assert.IsNotNull (monitor, "#A3-1");
310                         Assert.AreEqual ("System.Runtime.Caching.MemoryCacheEntryChangeMonitor", monitor.GetType ().ToString (), "#A3-2");
311                         Assert.AreEqual (1, monitor.CacheKeys.Count, "#A3-3");
312                         Assert.AreEqual ("key1", monitor.CacheKeys [0], "#A3-3-1");
313                         Assert.IsNull (monitor.RegionName, "#A3-4");
314                         Assert.IsTrue (monitor.HasChanged, "#A3-5");
315 #endif
316                 }
317
318                 [Test]
319                 public void AddOrGetExisting_String_Object_DateTimeOffset_String ()
320                 {
321                         var mc = new PokerMemoryCache ("MyCache");
322
323                         AssertExtensions.Throws<ArgumentNullException> (() => {
324                                 mc.AddOrGetExisting (null, "value", DateTimeOffset.Now);
325                         }, "#A1-1");
326
327                         AssertExtensions.Throws<ArgumentNullException> (() => {
328                                 mc.AddOrGetExisting ("key", null, DateTimeOffset.Now);
329                         }, "#A1-2");
330                         
331                         AssertExtensions.Throws<NotSupportedException> (() => {
332                                 mc.AddOrGetExisting ("key", "value", DateTimeOffset.Now, "region");
333                         }, "#A1-3");
334
335                         object value = mc.AddOrGetExisting ("key3_A2-1", "value", DateTimeOffset.Now.AddMinutes (1));
336                         Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A2-1");
337                         Assert.IsNull (value, "#A2-2");
338
339                         mc.Calls.Clear ();
340                         value = mc.AddOrGetExisting ("key3_A2-1", "value2", DateTimeOffset.Now.AddMinutes (1));
341                         Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A3-1");
342                         Assert.IsNotNull (value, "#A3-2");
343                         Assert.AreEqual ("value", value, "#A3-3");
344                         Assert.AreEqual (2, mc.Calls.Count, "#A3-4");
345                         Assert.AreEqual ("AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls [0], "#A3-5");
346
347                         value = mc.AddOrGetExisting ("key_expired", "value", DateTimeOffset.MinValue);
348                         Assert.IsFalse (mc.Contains ("key_expired"), "#A4-1");
349                         Assert.IsNull (value, "#A4-1");
350                 }
351
352                 [Test]
353                 public void AddOrGetExisting_String_Object_CacheItemPolicy_String ()
354                 {
355                         var mc = new PokerMemoryCache ("MyCache");
356
357                         AssertExtensions.Throws<ArgumentNullException> (() => {
358                                 mc.AddOrGetExisting (null, "value", null);
359                         }, "#A1-1");
360
361                         AssertExtensions.Throws<ArgumentNullException> (() => {
362                                 mc.AddOrGetExisting ("key", null, null);
363                         }, "#A1-2");
364
365                         var cip = new CacheItemPolicy ();
366                         cip.AbsoluteExpiration = DateTime.Now.AddMinutes (1);
367                         cip.SlidingExpiration = TimeSpan.FromMinutes (1);
368
369                         AssertExtensions.Throws<ArgumentException> (() => {
370                                 mc.AddOrGetExisting ("key", "value", cip);
371                         }, "#A1-3");
372
373                         cip = new CacheItemPolicy ();
374                         cip.SlidingExpiration = TimeSpan.MinValue;
375                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
376                                 mc.AddOrGetExisting ("key3", "value", cip);
377                         }, "#A1-4");
378
379                         AssertExtensions.Throws<NotSupportedException> (() => {
380                                 mc.AddOrGetExisting ("key", "value", null, "region");
381                         }, "#A1-5");
382
383                         cip = new CacheItemPolicy ();
384                         cip.SlidingExpiration = TimeSpan.FromDays (500);
385                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
386                                 mc.AddOrGetExisting ("key3", "value", cip);
387                         }, "#A1-6");
388
389                         cip = new CacheItemPolicy ();
390                         cip.Priority = (CacheItemPriority) 20;
391                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
392                                 mc.AddOrGetExisting ("key3", "value", cip);
393                         }, "#A1-7");
394
395                         cip = new CacheItemPolicy ();
396                         cip.SlidingExpiration = TimeSpan.FromTicks (0L);
397                         mc.AddOrGetExisting ("key3_A2-1", "value", cip);
398                         Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A2-1");
399
400                         cip = new CacheItemPolicy ();
401                         cip.SlidingExpiration = TimeSpan.FromDays (365);
402                         mc.AddOrGetExisting ("key3_A2-2", "value", cip);
403                         Assert.IsTrue (mc.Contains ("key3_A2-2"), "#A2-2");
404
405                         cip = new CacheItemPolicy ();
406                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
407                         object value = mc.AddOrGetExisting ("key3_A2-3", "value", cip);
408                         Assert.IsTrue (mc.Contains ("key3_A2-3"), "#A2-3");
409                         Assert.IsNull (value, "#A2-4");
410
411                         mc.Calls.Clear ();
412                         value = mc.AddOrGetExisting ("key3_A2-3", "value2", null);
413                         Assert.IsTrue (mc.Contains ("key3_A2-3"), "#A3-1");
414                         Assert.IsNotNull (value, "#A3-2");
415                         Assert.AreEqual ("value", value, "#A3-3");
416                         Assert.AreEqual (2, mc.Calls.Count, "#A3-4");
417                         Assert.AreEqual ("AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [0], "#A3-5");
418
419                         cip = new CacheItemPolicy ();
420                         cip.AbsoluteExpiration = DateTimeOffset.MinValue;
421                         value = mc.AddOrGetExisting ("key_expired", "value", cip);
422                         Assert.IsFalse (mc.Contains ("key_expired"), "#A4-1");
423                         Assert.IsNull (value, "#A4-1");
424                 }
425
426                 [Test]
427                 public void AddOrGetExisting_CacheItem_CacheItemPolicy ()
428                 {
429                         var mc = new PokerMemoryCache ("MyCache");
430                         CacheItem ci, ci2;
431
432                         AssertExtensions.Throws<ArgumentNullException> (() => {
433                                 ci = mc.AddOrGetExisting (null, new CacheItemPolicy ());
434                         }, "#A1");
435
436                         ci = new CacheItem ("key", "value");
437                         ci2 = mc.AddOrGetExisting (ci, null);
438
439                         // LAMESPEC: MSDN says it should return null if the entry does not exist yet.
440                         //
441                         Assert.IsNotNull (ci2, "#A2-1"); 
442                         Assert.AreNotEqual (ci, ci2, "#A2-2");
443                         Assert.IsNull (ci2.Value, "#A2-3");
444                         Assert.IsTrue (mc.Contains (ci.Key), "#A2-4");
445                         Assert.AreEqual (ci.Key, ci2.Key, "#A2-5");
446
447                         ci = new CacheItem ("key", "value");
448                         ci2 = mc.AddOrGetExisting (ci, null);
449                         Assert.IsNotNull (ci2, "#A3-1");
450                         Assert.AreNotEqual (ci, ci2, "#A3-2");
451                         Assert.IsNotNull (ci2.Value, "#A3-3");
452                         Assert.AreEqual (ci.Value, ci2.Value, "#A3-4");
453                         Assert.AreEqual (ci.Key, ci2.Key, "#A3-5");
454
455                         AssertExtensions.Throws<ArgumentNullException> (() => {
456                                 ci = new CacheItem (null, "value");
457                                 ci2 = mc.AddOrGetExisting (ci, null);
458                         }, "#A4");
459
460                         ci = new CacheItem (String.Empty, "value");
461                         ci2 = mc.AddOrGetExisting (ci, null);
462                         Assert.IsNotNull (ci2, "#A5-1");
463                         Assert.AreNotEqual (ci, ci2, "#A5-2");
464                         Assert.IsNull (ci2.Value, "#A5-3");
465                         Assert.IsTrue (mc.Contains (ci.Key), "#A5-4");
466                         Assert.AreEqual (ci.Key, ci2.Key, "#A5-5");
467
468                         ci = new CacheItem ("key2", null);
469
470                         // Thrown from:
471                         // at System.Runtime.Caching.MemoryCacheEntry..ctor(String key, Object value, DateTimeOffset absExp, TimeSpan slidingExp, CacheItemPriority priority, Collection`1 dependencies, CacheEntryRemovedCallback removedCallback, MemoryCache cache)
472                         // at System.Runtime.Caching.MemoryCache.AddOrGetExistingInternal(String key, Object value, CacheItemPolicy policy)
473                         // at System.Runtime.Caching.MemoryCache.AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
474                         // 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
475                         AssertExtensions.Throws<ArgumentNullException> (() => {
476                                 ci2 = mc.AddOrGetExisting (ci, null);
477                         }, "#B1");
478                         
479                         ci = new CacheItem ("key3", "value");
480                         var cip = new CacheItemPolicy ();
481                         cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
482                         AssertExtensions.Throws<ArgumentException> (() => {
483                                 ci2 = mc.AddOrGetExisting (ci, cip);
484                         }, "#B2");
485
486                         ci = new CacheItem ("key3", "value");
487                         cip = new CacheItemPolicy ();
488                         cip.AbsoluteExpiration = DateTimeOffset.Now;
489                         cip.SlidingExpiration = TimeSpan.FromTicks (DateTime.Now.Ticks);
490                         AssertExtensions.Throws<ArgumentException> (() => {
491                                 mc.AddOrGetExisting (ci, cip);
492                         }, "#B3");
493
494                         ci = new CacheItem ("key3", "value");
495                         cip = new CacheItemPolicy ();
496                         cip.SlidingExpiration = TimeSpan.MinValue;
497                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
498                                 mc.AddOrGetExisting (ci, cip);
499                         }, "#B4-1");
500
501                         ci = new CacheItem ("key4_#B4-2", "value");
502                         cip = new CacheItemPolicy ();
503                         cip.SlidingExpiration = TimeSpan.FromTicks (0L);
504                         mc.AddOrGetExisting (ci, cip);
505                         Assert.IsTrue (mc.Contains ("key4_#B4-2"), "#B4-2");
506
507                         ci = new CacheItem ("key3", "value");
508                         cip = new CacheItemPolicy ();
509                         cip.SlidingExpiration = TimeSpan.FromDays (500);
510                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
511                                 mc.AddOrGetExisting (ci, cip);
512                         }, "#B5-1");
513
514                         ci = new CacheItem ("key5_#B5-2", "value");
515                         cip = new CacheItemPolicy ();
516                         cip.SlidingExpiration = TimeSpan.FromDays (365);
517                         mc.AddOrGetExisting (ci, cip);
518                         Assert.IsTrue (mc.Contains ("key5_#B5-2"), "#B5-2");
519
520                         ci = new CacheItem ("key3", "value");
521                         cip = new CacheItemPolicy ();
522                         cip.Priority = (CacheItemPriority)20;
523                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
524                                 mc.AddOrGetExisting (ci, cip);
525                         }, "#B6");
526
527                         ci = new CacheItem ("key3_B7", "value");
528                         cip = new CacheItemPolicy ();
529                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
530                         ci2 = mc.AddOrGetExisting (ci, cip);
531                         Assert.IsTrue (mc.Contains ("key3_B7"), "#B7");
532
533                         // LAMESPEC: MSDN says it should return null if the entry does not exist yet.
534                         //
535                         Assert.IsNotNull (ci2, "#C1-1");
536                         Assert.AreNotEqual (ci, ci2, "#C1-2");
537                         Assert.IsNull (ci2.Value, "#C1-3");
538                         Assert.IsTrue (mc.Contains (ci.Key), "#C1-4");
539                         Assert.AreEqual (ci.Key, ci2.Key, "#C1-5");
540
541                         // The entry is never inserted as its expiration date is before now
542                         ci = new CacheItem ("key_D1", "value_D1");
543                         cip = new CacheItemPolicy ();
544                         cip.AbsoluteExpiration = DateTimeOffset.MinValue;
545                         ci2 = mc.AddOrGetExisting (ci, cip);
546                         Assert.IsFalse (mc.Contains ("key_D1"), "#D1-1");
547                         Assert.IsNotNull (ci2, "#D1-2");
548                         Assert.IsNull (ci2.Value, "#D1-3");
549                         Assert.AreEqual ("key_D1", ci2.Key, "#D1-4");
550
551                         mc.Calls.Clear ();
552                         ci = new CacheItem ("key_D2", "value_D2");
553                         cip = new CacheItemPolicy ();
554                         cip.AbsoluteExpiration = DateTimeOffset.MaxValue;
555                         mc.AddOrGetExisting (ci, cip);
556                         Assert.IsTrue (mc.Contains ("key_D2"), "#D2-1");
557                         Assert.AreEqual (2, mc.Calls.Count, "#D2-2");
558                         Assert.AreEqual ("AddOrGetExisting (CacheItem item, CacheItemPolicy policy)", mc.Calls [0], "#D2-3");
559                 }
560
561                 [Test]
562                 public void Set_String_Object_CacheItemPolicy_String ()
563                 {
564                         var mc = new PokerMemoryCache ("MyCache");
565
566                         AssertExtensions.Throws<NotSupportedException> (() => {
567                                 mc.Set ("key", "value", new CacheItemPolicy (), "region");
568                         }, "#A1-1");
569
570                         AssertExtensions.Throws<ArgumentNullException> (() => {
571                                 mc.Set (null, "value", new CacheItemPolicy ());
572                         }, "#A1-2");
573
574                         AssertExtensions.Throws<ArgumentNullException> (() => {
575                                 mc.Set ("key", null, new CacheItemPolicy ());
576                         }, "#A1-3");
577
578                         var cip = new CacheItemPolicy ();
579                         cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
580                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
581                         AssertExtensions.Throws<ArgumentException> (() => {
582                                 mc.Set ("key", "value", cip);
583                         }, "#A1-4");
584
585                         cip = new CacheItemPolicy ();
586                         cip.SlidingExpiration = TimeSpan.MinValue;
587                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
588                                 mc.Set ("key", "value", cip);
589                         }, "#A1-5");
590
591                         cip = new CacheItemPolicy ();
592                         cip.SlidingExpiration = TimeSpan.FromTicks (0L);
593                         mc.Set ("key_A1-6", "value", cip);
594                         Assert.IsTrue (mc.Contains ("key_A1-6"), "#A1-6");
595
596                         cip = new CacheItemPolicy ();
597                         cip.SlidingExpiration = TimeSpan.FromDays (500);
598                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
599                                 mc.Set ("key", "value", cip);
600                         }, "#A1-7");
601                         
602                         cip = new CacheItemPolicy ();
603                         cip.SlidingExpiration = TimeSpan.FromDays (365);
604                         mc.Set ("key_A1-8", "value", cip);
605                         Assert.IsTrue (mc.Contains ("key_A1-8"), "#A1-8");
606
607                         cip = new CacheItemPolicy ();
608                         cip.Priority = (CacheItemPriority) 20;
609                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
610                                 mc.Set ("key", "value", cip);
611                         }, "#A1-9");
612                         
613                         cip = new CacheItemPolicy ();
614                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
615                         mc.Set ("key_A2", "value_A2", cip);
616                         Assert.IsTrue (mc.Contains ("key_A2"), "#A2");
617
618                         mc.Set ("key_A3", "value_A3", new CacheItemPolicy ());
619                         Assert.IsTrue (mc.Contains ("key_A3"), "#A3-1");
620                         Assert.AreEqual ("value_A3", mc.Get ("key_A3"), "#A3-2");
621
622                         // The entry is never inserted as its expiration date is before now
623                         cip = new CacheItemPolicy ();
624                         cip.AbsoluteExpiration = DateTimeOffset.MinValue;
625                         mc.Set ("key_A4", "value_A4", cip);
626                         Assert.IsFalse (mc.Contains ("key_A4"), "#A4");
627
628                         mc.Calls.Clear ();
629                         cip = new CacheItemPolicy ();
630                         cip.AbsoluteExpiration = DateTimeOffset.MaxValue;
631                         mc.Set ("key_A5", "value_A5", cip);
632                         Assert.IsTrue (mc.Contains ("key_A5"), "#A5-1");
633                         Assert.AreEqual (2, mc.Calls.Count, "#A5-2");
634                         Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [0], "#A5-3");
635                 }
636
637                 [Test]
638                 public void Set_String_Object_DateTimeOffset_String ()
639                 {
640                         var mc = new PokerMemoryCache ("MyCache");
641
642                         AssertExtensions.Throws<NotSupportedException> (() => {
643                                 mc.Set ("key", "value", DateTimeOffset.MaxValue, "region");
644                         }, "#A1-1");
645
646                         AssertExtensions.Throws<ArgumentNullException> (() => {
647                                 mc.Set (null, "value", DateTimeOffset.MaxValue);
648                         }, "#A1-2");
649
650                         AssertExtensions.Throws<ArgumentNullException> (() => {
651                                 mc.Set ("key", null, DateTimeOffset.MaxValue);
652                         }, "#A1-3");
653                         
654                         // The entry is never inserted as its expiration date is before now
655                         mc.Set ("key_A2", "value_A2", DateTimeOffset.MinValue);
656                         Assert.IsFalse (mc.Contains ("key_A2"), "#A2");
657
658                         mc.Calls.Clear ();
659                         mc.Set ("key", "value", DateTimeOffset.MaxValue);
660
661                         Assert.AreEqual (2, mc.Calls.Count, "#A2-1");
662                         Assert.AreEqual ("Set (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls [0], "#A2-2");
663                         Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [1], "#A2-3");
664                 }
665
666                 [Test]
667                 public void Set_CacheItem_CacheItemPolicy ()
668                 {
669                         var mc = new PokerMemoryCache ("MyCache");
670
671                         AssertExtensions.Throws<ArgumentNullException> (() => {
672                                 mc.Set (null, new CacheItemPolicy ());
673                         }, "#A1-1");
674
675                         // Actually thrown from the Set (string, object, CacheItemPolicy, string) overload
676                         var ci = new CacheItem (null, "value");
677                         AssertExtensions.Throws<ArgumentNullException> (() => {
678                                 mc.Set (ci, new CacheItemPolicy ());
679                         }, "#A1-2");
680
681                         ci = new CacheItem ("key", null);
682                         AssertExtensions.Throws<ArgumentNullException> (() => {
683                                 mc.Set (ci, new CacheItemPolicy ());
684                         }, "#A1-3");
685
686                         ci = new CacheItem ("key", "value");
687                         var cip = new CacheItemPolicy ();
688                         cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
689                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
690                         AssertExtensions.Throws<ArgumentException> (() => {
691                                 mc.Set (ci, cip);
692                         }, "#A1-4");
693
694                         ci = new CacheItem ("key", "value");
695                         cip = new CacheItemPolicy ();
696                         cip.SlidingExpiration = TimeSpan.MinValue;
697                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
698                                 mc.Set (ci, cip);
699                         }, "#A1-5");
700
701                         ci = new CacheItem ("key_A1-6", "value");
702                         cip = new CacheItemPolicy ();
703                         cip.SlidingExpiration = TimeSpan.FromTicks (0L);
704                         mc.Set (ci, cip);
705                         Assert.IsTrue (mc.Contains ("key_A1-6"), "#A1-6");
706
707                         ci = new CacheItem ("key", "value");
708                         cip = new CacheItemPolicy ();
709                         cip.SlidingExpiration = TimeSpan.FromDays (500);
710                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
711                                 mc.Set (ci, cip);
712                         }, "#A1-7");
713
714                         ci = new CacheItem ("key_A1-8", "value");
715                         cip = new CacheItemPolicy ();
716                         cip.SlidingExpiration = TimeSpan.FromDays (365);
717                         mc.Set (ci, cip);
718                         Assert.IsTrue (mc.Contains ("key_A1-8"), "#A1-8");
719
720                         ci = new CacheItem ("key", "value");
721                         cip = new CacheItemPolicy ();
722                         cip.Priority = (CacheItemPriority) 20;
723                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
724                                 mc.Set (ci, cip);
725                         }, "#A1-9");
726
727                         ci = new CacheItem ("key_A2", "value_A2");
728                         cip = new CacheItemPolicy ();
729                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
730                         mc.Set (ci, cip);
731                         Assert.IsTrue (mc.Contains ("key_A2"), "#A2");
732
733                         ci = new CacheItem ("key_A3", "value_A3");
734                         mc.Set (ci, new CacheItemPolicy ());
735                         Assert.IsTrue (mc.Contains ("key_A3"), "#A3-1");
736                         Assert.AreEqual ("value_A3", mc.Get ("key_A3"), "#A3-2");
737
738                         // The entry is never inserted as its expiration date is before now
739                         ci = new CacheItem ("key_A4", "value");
740                         cip = new CacheItemPolicy ();
741                         cip.AbsoluteExpiration = DateTimeOffset.MinValue;
742                         mc.Set (ci, cip);
743                         Assert.IsFalse (mc.Contains ("key_A4"), "#A4");
744
745                         ci = new CacheItem ("key_A5", "value");
746                         mc.Calls.Clear ();
747                         mc.Set (ci, new CacheItemPolicy ());
748
749                         Assert.AreEqual (2, mc.Calls.Count, "#A5-1");
750                         Assert.AreEqual ("Set (CacheItem item, CacheItemPolicy policy)", mc.Calls [0], "#A5-2");
751                         Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [1], "#A5-3");
752                 }
753
754                 [Test]
755                 public void Remove ()
756                 {
757                         var mc = new PokerMemoryCache ("MyCache");
758                 
759                         AssertExtensions.Throws<NotSupportedException> (() => {
760                                 mc.Remove ("key", "region");
761                         }, "#A1-1");
762
763                         AssertExtensions.Throws<ArgumentNullException> (() => {
764                                 mc.Remove (null);
765                         }, "#A1-2");
766
767                         bool callbackInvoked;
768                         CacheEntryRemovedReason reason = (CacheEntryRemovedReason) 1000;
769                         var cip = new CacheItemPolicy ();
770                         cip.Priority = CacheItemPriority.NotRemovable;
771                         mc.Set ("key2", "value1", cip);
772                         object value = mc.Remove ("key2");
773
774                         Assert.IsNotNull (value, "#B1-1");
775                         Assert.IsFalse (mc.Contains ("key2"), "#B1-2");
776
777                         cip = new CacheItemPolicy ();
778                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
779                                 callbackInvoked = true;
780                                 reason = args.RemovedReason;
781                         };
782
783                         mc.Set ("key", "value", cip);
784                         callbackInvoked = false;
785                         reason = (CacheEntryRemovedReason) 1000;
786                         value = mc.Remove ("key");
787                         Assert.IsNotNull (value, "#C1-1");
788                         Assert.IsTrue (callbackInvoked, "#C1-2");
789                         Assert.AreEqual (CacheEntryRemovedReason.Removed, reason, "#C1-3");
790
791                         cip = new CacheItemPolicy ();
792                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
793                                 callbackInvoked = true;
794                                 reason = args.RemovedReason;
795                                 throw new ApplicationException ("test");
796                         };
797
798                         mc.Set ("key", "value", cip);
799                         callbackInvoked = false;
800                         reason = (CacheEntryRemovedReason) 1000;
801                         value = mc.Remove ("key");
802                         Assert.IsNotNull (value, "#C2-1");
803                         Assert.IsTrue (callbackInvoked, "#C2-2");
804                         Assert.AreEqual (CacheEntryRemovedReason.Removed, reason, "#C2-3");
805
806                         // LAMESPEC: UpdateCallback is not called on remove
807                         cip = new CacheItemPolicy ();
808                         cip.UpdateCallback = (CacheEntryUpdateArguments args) => {
809                                 callbackInvoked = true;
810                                 reason = args.RemovedReason;
811                         };
812
813                         mc.Set ("key", "value", cip);
814                         callbackInvoked = false;
815                         reason = (CacheEntryRemovedReason) 1000;
816                         value = mc.Remove ("key");
817                         Assert.IsNotNull (value, "#D1-1");
818                         Assert.IsFalse (callbackInvoked, "#D1-2");
819
820                         cip = new CacheItemPolicy ();
821                         cip.UpdateCallback = (CacheEntryUpdateArguments args) => {
822                                 callbackInvoked = true;
823                                 reason = args.RemovedReason;
824                                 throw new ApplicationException ("test");
825                         };
826
827                         mc.Set ("key", "value", cip);
828                         callbackInvoked = false;
829                         reason = (CacheEntryRemovedReason) 1000;
830                         value = mc.Remove ("key");
831                         Assert.IsNotNull (value, "#D2-1");
832                         Assert.IsFalse (callbackInvoked, "#D2-2");
833                 }
834
835                 [Test]
836                 public void TimedExpiration ()
837                 {
838                         bool expired = false;
839                         CacheEntryRemovedReason reason = CacheEntryRemovedReason.CacheSpecificEviction;
840                         NameValueCollection config;
841                         int sleepPeriod;
842 #if !DOTNET
843                         config = new NameValueCollection ();
844                         config.Add ("__MonoTimerPeriod", "1");
845                         sleepPeriod = 1100;
846 #else
847                         config = null;
848                         sleepPeriod = 20100; // 20s is the .NET period - discovered by experimentation
849 #endif
850                         var mc = new PokerMemoryCache ("MyCache", config);
851                         var cip = new CacheItemPolicy ();
852
853                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
854                                 expired = true;
855                                 reason = args.RemovedReason;
856                         };
857                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
858                         mc.Set ("key", "value", cip);
859                         Thread.Sleep (100);
860
861                         Assert.IsFalse (expired, "#A1");
862                         object value = mc.Get ("key");
863
864                         Assert.IsNull (value, "#A2-1");
865                         Assert.IsTrue (expired, "#A2-2");
866                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "A2-3");
867
868                         expired = false;
869                         cip = new CacheItemPolicy ();
870                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
871                                 expired = true;
872                                 reason = args.RemovedReason;
873                         };
874                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
875                         mc.Set ("key", "value", cip);
876                         Thread.Sleep (sleepPeriod);
877
878                         Assert.IsTrue (expired, "#A3-1");
879                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#A3-2");
880
881                         int expiredCount = 0;
882                         object expiredCountLock = new object ();
883                         CacheEntryRemovedCallback removedCb = (CacheEntryRemovedArguments args) => {
884                                 lock (expiredCountLock) {
885                                         expiredCount++;
886                                 }
887                         };
888
889                         cip = new CacheItemPolicy ();
890                         cip.RemovedCallback = removedCb;
891                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (20);
892                         mc.Set ("key1", "value1", cip);
893
894                         cip = new CacheItemPolicy ();
895                         cip.RemovedCallback = removedCb;
896                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (200);
897                         mc.Set ("key2", "value2", cip);
898
899                         cip = new CacheItemPolicy ();
900                         cip.RemovedCallback = removedCb;
901                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (600);
902                         mc.Set ("key3", "value3", cip);
903
904                         cip = new CacheItemPolicy ();
905                         cip.RemovedCallback = removedCb;
906                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (sleepPeriod + 100);
907                         mc.Set ("key4", "value4", cip);
908                         
909                         Thread.Sleep (sleepPeriod);
910                         Assert.AreEqual (3, expiredCount, "#A4");
911                 }
912
913                 [Test]
914                 public void GetEnumerator ()
915                 {
916                         var mc = new PokerMemoryCache ("MyCache");
917
918                         // This one is a Hashtable enumerator
919                         IEnumerator enumerator = ((IEnumerable) mc).GetEnumerator ();
920
921                         // This one is a Dictionary <string, object> enumerator
922                         IEnumerator enumerator2 = mc.DoGetEnumerator ();
923
924                         Assert.IsNotNull (enumerator, "#A1-1");
925                         Assert.IsNotNull (enumerator2, "#A1-2");
926                         Assert.IsTrue (enumerator.GetType () != enumerator2.GetType (), "#A1-3");
927
928                         mc.Set ("key1", "value1", null);
929                         mc.Set ("key2", "value2", null);
930                         mc.Set ("key3", "value3", null);
931
932                         bool expired = false;
933                         var cip = new CacheItemPolicy ();
934                         cip.AbsoluteExpiration = DateTime.Now.AddMilliseconds (50);
935                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
936                                 expired = true;
937                         };
938
939                         mc.Set ("key4", "value4", cip);
940                         Thread.Sleep (100);
941
942                         enumerator = ((IEnumerable) mc).GetEnumerator ();
943                         int count = 0;
944                         while (enumerator.MoveNext ()) {
945                                 count++;
946                         }
947
948                         Assert.IsFalse (expired, "#A2-1");
949                         Assert.AreEqual (3, count, "#A2-2");
950
951                         expired = false;
952                         cip = new CacheItemPolicy ();
953                         cip.AbsoluteExpiration = DateTime.Now.AddMilliseconds (50);
954                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
955                                 expired = true;
956                         };
957
958                         mc.Set ("key5", "value5", cip);
959                         Thread.Sleep (100);
960
961                         enumerator2 = mc.DoGetEnumerator ();
962                         count = 0;
963                         while (enumerator2.MoveNext ()) {
964                                 count++;
965                         }
966
967                         Assert.IsFalse (expired, "#A3-1");
968                         Assert.AreEqual (3, count, "#A3-2");
969                 }
970
971                 [Test]
972                 public void GetValues ()
973                 {
974                         var mc = new PokerMemoryCache ("MyCache");
975
976                         AssertExtensions.Throws<ArgumentNullException> (() => {
977                                 mc.GetValues (null);
978                         }, "#A1-1");
979
980                         AssertExtensions.Throws<NotSupportedException> (() => {
981                                 mc.GetValues (new string[] {}, "region");
982                         }, "#A1-2");
983
984                         AssertExtensions.Throws<ArgumentException> (() => {
985                                 mc.GetValues (new string [] { "key", null });
986                         }, "#A1-3");
987
988                         IDictionary<string, object> value = mc.GetValues (new string[] {});
989                         Assert.IsNull (value, "#A2");
990
991                         mc.Set ("key1", "value1", null);
992                         mc.Set ("key2", "value2", null);
993                         mc.Set ("key3", "value3", null);
994
995                         Assert.IsTrue (mc.Contains ("key1"), "#A3-1");
996                         Assert.IsTrue (mc.Contains ("key2"), "#A3-2");
997                         Assert.IsTrue (mc.Contains ("key3"), "#A3-2");
998
999                         value = mc.GetValues (new string [] { "key1", "key3" });
1000                         Assert.IsNotNull (value, "#A4-1");
1001                         Assert.AreEqual (2, value.Count, "#A4-2");
1002                         Assert.AreEqual ("value1", value ["key1"], "#A4-3");
1003                         Assert.AreEqual ("value3", value ["key3"], "#A4-4");
1004                         Assert.AreEqual (typeof (Dictionary<string, object>), value.GetType (), "#A4-5");
1005
1006                         // LAMESPEC: MSDN says the number of items in the returned dictionary should be the same as in the 
1007                         // 'keys' collection - this is not the case. The returned dictionary contains only entries for keys
1008                         // that exist in the cache.
1009                         value = mc.GetValues (new string [] { "key1", "key3", "nosuchkey" });
1010                         Assert.IsNotNull (value, "#A5-1");
1011                         Assert.AreEqual (2, value.Count, "#A5-2");
1012                         Assert.AreEqual ("value1", value ["key1"], "#A5-3");
1013                         Assert.AreEqual ("value3", value ["key3"], "#A5-4");
1014                         Assert.IsFalse (value.ContainsKey ("Key1"), "#A5-5");
1015                 }
1016
1017                 [Test]
1018                 public void Get ()
1019                 {
1020                         var mc = new PokerMemoryCache ("MyCache");
1021
1022                         AssertExtensions.Throws<NotSupportedException> (() => {
1023                                 mc.Get ("key", "region");
1024                         }, "#A1-1");
1025
1026                         AssertExtensions.Throws<ArgumentNullException> (() => {
1027                                 mc.Get (null);
1028                         }, "#A1-2");
1029
1030                         object value;
1031                         mc.Set ("key", "value", null);
1032                         value = mc.Get ("key");
1033                         Assert.IsNotNull (value, "#A2-1");
1034                         Assert.AreEqual ("value", value, "#A2-2");
1035
1036                         value = mc.Get ("nosuchkey");
1037                         Assert.IsNull (value, "#A3");
1038
1039                         var cip = new CacheItemPolicy ();
1040                         bool callbackInvoked;
1041                         CacheEntryRemovedReason reason = (CacheEntryRemovedReason)1000;
1042
1043                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1044                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1045                                 callbackInvoked = true;
1046                                 reason = args.RemovedReason;
1047                         };
1048                         mc.Set ("key", "value", cip);
1049                         Thread.Sleep (500);
1050
1051                         callbackInvoked = false;
1052                         reason = (CacheEntryRemovedReason) 1000;
1053                         value = mc.Get ("key");
1054                         Assert.IsNull (value, "#B1-1");
1055                         Assert.IsTrue (callbackInvoked, "#B1-2");
1056                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B1-3");
1057
1058                         cip = new CacheItemPolicy ();
1059                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1060                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1061                                 callbackInvoked = true;
1062                                 reason = args.RemovedReason;
1063                                 throw new ApplicationException ("test");
1064                         };
1065
1066                         mc.Set ("key", "value", cip);
1067                         Thread.Sleep (500);
1068
1069                         callbackInvoked = false;
1070                         reason = (CacheEntryRemovedReason) 1000;
1071                         value = mc.Get ("key");
1072                         Assert.IsNull (value, "#B2-1");
1073                         Assert.IsTrue (callbackInvoked, "#B2-2");
1074                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B2-3");
1075                 }
1076
1077                 [Test]
1078                 public void GetCacheItem ()
1079                 {
1080                         var mc = new PokerMemoryCache ("MyCache");
1081
1082                         AssertExtensions.Throws<NotSupportedException> (() => {
1083                                 mc.GetCacheItem ("key", "region");
1084                         }, "#A1-1");
1085
1086                         AssertExtensions.Throws<ArgumentNullException> (() => {
1087                                 mc.GetCacheItem (null);
1088                         }, "#A1-2");
1089
1090                         CacheItem value;
1091                         mc.Set ("key", "value", null);
1092                         value = mc.GetCacheItem ("key");
1093                         Assert.IsNotNull (value, "#A2-1");
1094                         Assert.AreEqual ("value", value.Value, "#A2-2");
1095                         Assert.AreEqual ("key", value.Key, "#A2-3");
1096
1097                         value = mc.GetCacheItem ("doesnotexist");
1098                         Assert.IsNull (value, "#A3");
1099
1100                         var cip = new CacheItemPolicy ();
1101                         bool callbackInvoked;
1102                         CacheEntryRemovedReason reason = (CacheEntryRemovedReason) 1000;
1103
1104                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1105                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1106                                 callbackInvoked = true;
1107                                 reason = args.RemovedReason;
1108                         };
1109                         mc.Set ("key", "value", cip);
1110                         Thread.Sleep (500);
1111
1112                         callbackInvoked = false;
1113                         reason = (CacheEntryRemovedReason) 1000;
1114                         value = mc.GetCacheItem ("key");
1115                         Assert.IsNull (value, "#B1-1");
1116                         Assert.IsTrue (callbackInvoked, "#B1-2");
1117                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B1-3");
1118
1119                         cip = new CacheItemPolicy ();
1120                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1121                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1122                                 callbackInvoked = true;
1123                                 reason = args.RemovedReason;
1124                                 throw new ApplicationException ("test");
1125                         };
1126
1127                         mc.Set ("key", "value", cip);
1128                         Thread.Sleep (500);
1129
1130                         callbackInvoked = false;
1131                         reason = (CacheEntryRemovedReason) 1000;
1132                         value = mc.GetCacheItem ("key");
1133                         Assert.IsNull (value, "#B2-1");
1134                         Assert.IsTrue (callbackInvoked, "#B2-2");
1135                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B2-3");
1136                 }
1137
1138                 [Test]
1139                 public void ChangeMonitors ()
1140                 {
1141                         bool removed = false;
1142                         var mc = new PokerMemoryCache ("MyCache");
1143                         var cip = new CacheItemPolicy ();
1144                         var monitor = new PokerChangeMonitor ();
1145                         cip.ChangeMonitors.Add (monitor);
1146                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1147                                 removed = true;
1148                         };
1149
1150                         mc.Set ("key", "value", cip);
1151                         Assert.AreEqual (0, monitor.Calls.Count, "#A1");
1152
1153                         monitor.SignalChange ();
1154                         Assert.IsTrue (removed, "#A2");
1155
1156                         bool onChangedCalled = false;
1157                         monitor = new PokerChangeMonitor ();
1158                         monitor.NotifyOnChanged ((object state) => {
1159                                 onChangedCalled = true;
1160                         });
1161
1162                         cip = new CacheItemPolicy ();
1163                         cip.ChangeMonitors.Add (monitor);
1164
1165                         // Thrown by ChangeMonitor.NotifyOnChanged
1166                         AssertExtensions.Throws<InvalidOperationException> (() => {
1167                                 mc.Set ("key1", "value1", cip);
1168                         }, "#A3");
1169                 }
1170
1171                 // NOTE: on Windows with 2 or more CPUs this test will most probably fail.
1172                 [Test]
1173                 public void Trim ()
1174                 {
1175                         var config = new NameValueCollection ();
1176                         config ["__MonoEmulateOneCPU"] = "true";
1177                         var mc = new MemoryCache ("MyCache", config);
1178
1179                         for (int i = 0; i < 10; i++)
1180                                 mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1181
1182                         // .NET doesn't touch the freshest 10 entries
1183                         Assert.AreEqual (10, mc.GetCount (), "#A1-1");
1184                         long trimmed = mc.Trim (50);
1185                         Assert.AreEqual (0, trimmed, "#A1-2");
1186                         Assert.AreEqual (10, mc.GetCount (), "#A1-3");
1187
1188                         mc = new MemoryCache ("MyCache", config);
1189                         // Only entries 11- are considered for removal
1190                         for (int i = 0; i < 11; i++)
1191                                 mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1192
1193                         Assert.AreEqual (11, mc.GetCount (), "#A2-1");
1194                         trimmed = mc.Trim (50);
1195                         Assert.AreEqual (1, trimmed, "#A2-2");
1196                         Assert.AreEqual (10, mc.GetCount (), "#A2-3");
1197
1198                         mc = new MemoryCache ("MyCache", config);
1199                         // Only entries 11- are considered for removal
1200                         for (int i = 0; i < 125; i++)
1201                                 mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1202
1203                         Assert.AreEqual (125, mc.GetCount (), "#A3-1");
1204                         trimmed = mc.Trim (50);
1205                         Assert.AreEqual (62, trimmed, "#A3-2");
1206                         Assert.AreEqual (63, mc.GetCount (), "#A3-3");
1207
1208                         // Testing the removal order
1209                         mc = new MemoryCache ("MyCache", config);
1210                         var removed = new List <string> ();
1211                         var cip = new CacheItemPolicy ();
1212                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1213                                 removed.Add (args.CacheItem.Key);
1214                         };
1215
1216                         for (int i = 0; i < 50; i++)
1217                                 mc.Set ("key" + i.ToString (), "value" + i.ToString (), cip);
1218
1219                         object value;
1220                         for (int i = 0; i < 50; i++)
1221                                 value = mc.Get ("key" + i.ToString ());
1222
1223                         trimmed = mc.Trim (50);
1224                         Assert.AreEqual (25, mc.GetCount (), "#A4-1");
1225                         Assert.AreEqual (25, trimmed, "#A4-2");
1226                         Assert.AreEqual (25, removed.Count, "#A4-3");
1227
1228                         // OK, this is odd... The list is correct in terms of entries removed but the entries
1229                         // are removed in the _MOST_ frequently used order, within the group selected for removal.
1230                         for (int i = 24; i >= 0; i--) {
1231                                 int idx = 24 - i;
1232                                 Assert.AreEqual ("key" + i.ToString (), removed [idx], "#A5-" + idx.ToString ());
1233                         }
1234                 }
1235         }
1236 }