New tests.
[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 true
308                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
309                                 monitor = mc.CreateCacheEntryChangeMonitor (new string [] { "key1", "doesnotexist" });
310                         }, "#A3");
311 #else
312                         monitor = mc.CreateCacheEntryChangeMonitor (new string [] { "key1", "doesnotexist" });
313                         Assert.IsNotNull (monitor, "#A3-1");
314                         Assert.AreEqual ("System.Runtime.Caching.MemoryCacheEntryChangeMonitor", monitor.GetType ().ToString (), "#A3-2");
315                         Assert.AreEqual (1, monitor.CacheKeys.Count, "#A3-3");
316                         Assert.AreEqual ("key1", monitor.CacheKeys [0], "#A3-3-1");
317                         Assert.IsNull (monitor.RegionName, "#A3-4");
318                         Assert.IsTrue (monitor.HasChanged, "#A3-5");
319 #endif
320                 }
321
322                 [Test]
323                 public void AddOrGetExisting_String_Object_DateTimeOffset_String ()
324                 {
325                         var mc = new PokerMemoryCache ("MyCache");
326
327                         AssertExtensions.Throws<ArgumentNullException> (() => {
328                                 mc.AddOrGetExisting (null, "value", DateTimeOffset.Now);
329                         }, "#A1-1");
330
331                         AssertExtensions.Throws<ArgumentNullException> (() => {
332                                 mc.AddOrGetExisting ("key", null, DateTimeOffset.Now);
333                         }, "#A1-2");
334                         
335                         AssertExtensions.Throws<NotSupportedException> (() => {
336                                 mc.AddOrGetExisting ("key", "value", DateTimeOffset.Now, "region");
337                         }, "#A1-3");
338
339                         object value = mc.AddOrGetExisting ("key3_A2-1", "value", DateTimeOffset.Now.AddMinutes (1));
340                         Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A2-1");
341                         Assert.IsNull (value, "#A2-2");
342
343                         mc.Calls.Clear ();
344                         value = mc.AddOrGetExisting ("key3_A2-1", "value2", DateTimeOffset.Now.AddMinutes (1));
345                         Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A3-1");
346                         Assert.IsNotNull (value, "#A3-2");
347                         Assert.AreEqual ("value", value, "#A3-3");
348                         Assert.AreEqual (2, mc.Calls.Count, "#A3-4");
349                         Assert.AreEqual ("AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls [0], "#A3-5");
350
351                         value = mc.AddOrGetExisting ("key_expired", "value", DateTimeOffset.MinValue);
352                         Assert.IsFalse (mc.Contains ("key_expired"), "#A4-1");
353                         Assert.IsNull (value, "#A4-1");
354                 }
355
356                 [Test]
357                 public void AddOrGetExisting_String_Object_CacheItemPolicy_String ()
358                 {
359                         var mc = new PokerMemoryCache ("MyCache");
360
361                         AssertExtensions.Throws<ArgumentNullException> (() => {
362                                 mc.AddOrGetExisting (null, "value", null);
363                         }, "#A1-1");
364
365                         AssertExtensions.Throws<ArgumentNullException> (() => {
366                                 mc.AddOrGetExisting ("key", null, null);
367                         }, "#A1-2");
368
369                         var cip = new CacheItemPolicy ();
370                         cip.AbsoluteExpiration = DateTime.Now.AddMinutes (1);
371                         cip.SlidingExpiration = TimeSpan.FromMinutes (1);
372
373                         AssertExtensions.Throws<ArgumentException> (() => {
374                                 mc.AddOrGetExisting ("key", "value", cip);
375                         }, "#A1-3");
376
377                         cip = new CacheItemPolicy ();
378                         cip.SlidingExpiration = TimeSpan.MinValue;
379                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
380                                 mc.AddOrGetExisting ("key3", "value", cip);
381                         }, "#A1-4");
382
383                         AssertExtensions.Throws<NotSupportedException> (() => {
384                                 mc.AddOrGetExisting ("key", "value", null, "region");
385                         }, "#A1-5");
386
387                         cip = new CacheItemPolicy ();
388                         cip.SlidingExpiration = TimeSpan.FromDays (500);
389                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
390                                 mc.AddOrGetExisting ("key3", "value", cip);
391                         }, "#A1-6");
392
393                         cip = new CacheItemPolicy ();
394                         cip.Priority = (CacheItemPriority) 20;
395                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
396                                 mc.AddOrGetExisting ("key3", "value", cip);
397                         }, "#A1-7");
398
399                         cip = new CacheItemPolicy ();
400                         cip.SlidingExpiration = TimeSpan.FromTicks (0L);
401                         mc.AddOrGetExisting ("key3_A2-1", "value", cip);
402                         Assert.IsTrue (mc.Contains ("key3_A2-1"), "#A2-1");
403
404                         cip = new CacheItemPolicy ();
405                         cip.SlidingExpiration = TimeSpan.FromDays (365);
406                         mc.AddOrGetExisting ("key3_A2-2", "value", cip);
407                         Assert.IsTrue (mc.Contains ("key3_A2-2"), "#A2-2");
408
409                         cip = new CacheItemPolicy ();
410                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
411                         object value = mc.AddOrGetExisting ("key3_A2-3", "value", cip);
412                         Assert.IsTrue (mc.Contains ("key3_A2-3"), "#A2-3");
413                         Assert.IsNull (value, "#A2-4");
414
415                         mc.Calls.Clear ();
416                         value = mc.AddOrGetExisting ("key3_A2-3", "value2", null);
417                         Assert.IsTrue (mc.Contains ("key3_A2-3"), "#A3-1");
418                         Assert.IsNotNull (value, "#A3-2");
419                         Assert.AreEqual ("value", value, "#A3-3");
420                         Assert.AreEqual (2, mc.Calls.Count, "#A3-4");
421                         Assert.AreEqual ("AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [0], "#A3-5");
422
423                         cip = new CacheItemPolicy ();
424                         cip.AbsoluteExpiration = DateTimeOffset.MinValue;
425                         value = mc.AddOrGetExisting ("key_expired", "value", cip);
426                         Assert.IsFalse (mc.Contains ("key_expired"), "#A4-1");
427                         Assert.IsNull (value, "#A4-1");
428                 }
429
430                 [Test]
431                 public void AddOrGetExisting_CacheItem_CacheItemPolicy ()
432                 {
433                         var mc = new PokerMemoryCache ("MyCache");
434                         CacheItem ci, ci2;
435
436                         AssertExtensions.Throws<ArgumentNullException> (() => {
437                                 ci = mc.AddOrGetExisting (null, new CacheItemPolicy ());
438                         }, "#A1");
439
440                         ci = new CacheItem ("key", "value");
441                         ci2 = mc.AddOrGetExisting (ci, null);
442
443                         // LAMESPEC: MSDN says it should return null if the entry does not exist yet.
444                         //
445                         Assert.IsNotNull (ci2, "#A2-1"); 
446                         Assert.AreNotEqual (ci, ci2, "#A2-2");
447                         Assert.IsNull (ci2.Value, "#A2-3");
448                         Assert.IsTrue (mc.Contains (ci.Key), "#A2-4");
449                         Assert.AreEqual (ci.Key, ci2.Key, "#A2-5");
450
451                         ci = new CacheItem ("key", "value");
452                         ci2 = mc.AddOrGetExisting (ci, null);
453                         Assert.IsNotNull (ci2, "#A3-1");
454                         Assert.AreNotEqual (ci, ci2, "#A3-2");
455                         Assert.IsNotNull (ci2.Value, "#A3-3");
456                         Assert.AreEqual (ci.Value, ci2.Value, "#A3-4");
457                         Assert.AreEqual (ci.Key, ci2.Key, "#A3-5");
458
459                         AssertExtensions.Throws<ArgumentNullException> (() => {
460                                 ci = new CacheItem (null, "value");
461                                 ci2 = mc.AddOrGetExisting (ci, null);
462                         }, "#A4");
463
464                         ci = new CacheItem (String.Empty, "value");
465                         ci2 = mc.AddOrGetExisting (ci, null);
466                         Assert.IsNotNull (ci2, "#A5-1");
467                         Assert.AreNotEqual (ci, ci2, "#A5-2");
468                         Assert.IsNull (ci2.Value, "#A5-3");
469                         Assert.IsTrue (mc.Contains (ci.Key), "#A5-4");
470                         Assert.AreEqual (ci.Key, ci2.Key, "#A5-5");
471
472                         ci = new CacheItem ("key2", null);
473
474                         // Thrown from:
475                         // at System.Runtime.Caching.MemoryCacheEntry..ctor(String key, Object value, DateTimeOffset absExp, TimeSpan slidingExp, CacheItemPriority priority, Collection`1 dependencies, CacheEntryRemovedCallback removedCallback, MemoryCache cache)
476                         // at System.Runtime.Caching.MemoryCache.AddOrGetExistingInternal(String key, Object value, CacheItemPolicy policy)
477                         // at System.Runtime.Caching.MemoryCache.AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
478                         // 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
479                         AssertExtensions.Throws<ArgumentNullException> (() => {
480                                 ci2 = mc.AddOrGetExisting (ci, null);
481                         }, "#B1");
482                         
483                         ci = new CacheItem ("key3", "value");
484                         var cip = new CacheItemPolicy ();
485                         cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
486                         AssertExtensions.Throws<ArgumentException> (() => {
487                                 ci2 = mc.AddOrGetExisting (ci, cip);
488                         }, "#B2");
489
490                         ci = new CacheItem ("key3", "value");
491                         cip = new CacheItemPolicy ();
492                         cip.AbsoluteExpiration = DateTimeOffset.Now;
493                         cip.SlidingExpiration = TimeSpan.FromTicks (DateTime.Now.Ticks);
494                         AssertExtensions.Throws<ArgumentException> (() => {
495                                 mc.AddOrGetExisting (ci, cip);
496                         }, "#B3");
497
498                         ci = new CacheItem ("key3", "value");
499                         cip = new CacheItemPolicy ();
500                         cip.SlidingExpiration = TimeSpan.MinValue;
501                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
502                                 mc.AddOrGetExisting (ci, cip);
503                         }, "#B4-1");
504
505                         ci = new CacheItem ("key4_#B4-2", "value");
506                         cip = new CacheItemPolicy ();
507                         cip.SlidingExpiration = TimeSpan.FromTicks (0L);
508                         mc.AddOrGetExisting (ci, cip);
509                         Assert.IsTrue (mc.Contains ("key4_#B4-2"), "#B4-2");
510
511                         ci = new CacheItem ("key3", "value");
512                         cip = new CacheItemPolicy ();
513                         cip.SlidingExpiration = TimeSpan.FromDays (500);
514                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
515                                 mc.AddOrGetExisting (ci, cip);
516                         }, "#B5-1");
517
518                         ci = new CacheItem ("key5_#B5-2", "value");
519                         cip = new CacheItemPolicy ();
520                         cip.SlidingExpiration = TimeSpan.FromDays (365);
521                         mc.AddOrGetExisting (ci, cip);
522                         Assert.IsTrue (mc.Contains ("key5_#B5-2"), "#B5-2");
523
524                         ci = new CacheItem ("key3", "value");
525                         cip = new CacheItemPolicy ();
526                         cip.Priority = (CacheItemPriority)20;
527                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
528                                 mc.AddOrGetExisting (ci, cip);
529                         }, "#B6");
530
531                         ci = new CacheItem ("key3_B7", "value");
532                         cip = new CacheItemPolicy ();
533                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
534                         ci2 = mc.AddOrGetExisting (ci, cip);
535                         Assert.IsTrue (mc.Contains ("key3_B7"), "#B7");
536
537                         // LAMESPEC: MSDN says it should return null if the entry does not exist yet.
538                         //
539                         Assert.IsNotNull (ci2, "#C1-1");
540                         Assert.AreNotEqual (ci, ci2, "#C1-2");
541                         Assert.IsNull (ci2.Value, "#C1-3");
542                         Assert.IsTrue (mc.Contains (ci.Key), "#C1-4");
543                         Assert.AreEqual (ci.Key, ci2.Key, "#C1-5");
544
545                         // The entry is never inserted as its expiration date is before now
546                         ci = new CacheItem ("key_D1", "value_D1");
547                         cip = new CacheItemPolicy ();
548                         cip.AbsoluteExpiration = DateTimeOffset.MinValue;
549                         ci2 = mc.AddOrGetExisting (ci, cip);
550                         Assert.IsFalse (mc.Contains ("key_D1"), "#D1-1");
551                         Assert.IsNotNull (ci2, "#D1-2");
552                         Assert.IsNull (ci2.Value, "#D1-3");
553                         Assert.AreEqual ("key_D1", ci2.Key, "#D1-4");
554
555                         mc.Calls.Clear ();
556                         ci = new CacheItem ("key_D2", "value_D2");
557                         cip = new CacheItemPolicy ();
558                         cip.AbsoluteExpiration = DateTimeOffset.MaxValue;
559                         mc.AddOrGetExisting (ci, cip);
560                         Assert.IsTrue (mc.Contains ("key_D2"), "#D2-1");
561                         Assert.AreEqual (2, mc.Calls.Count, "#D2-2");
562                         Assert.AreEqual ("AddOrGetExisting (CacheItem item, CacheItemPolicy policy)", mc.Calls [0], "#D2-3");
563                 }
564
565                 [Test]
566                 public void Set_String_Object_CacheItemPolicy_String ()
567                 {
568                         var mc = new PokerMemoryCache ("MyCache");
569
570                         AssertExtensions.Throws<NotSupportedException> (() => {
571                                 mc.Set ("key", "value", new CacheItemPolicy (), "region");
572                         }, "#A1-1");
573
574                         AssertExtensions.Throws<ArgumentNullException> (() => {
575                                 mc.Set (null, "value", new CacheItemPolicy ());
576                         }, "#A1-2");
577
578                         AssertExtensions.Throws<ArgumentNullException> (() => {
579                                 mc.Set ("key", null, new CacheItemPolicy ());
580                         }, "#A1-3");
581
582                         var cip = new CacheItemPolicy ();
583                         cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
584                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
585                         AssertExtensions.Throws<ArgumentException> (() => {
586                                 mc.Set ("key", "value", cip);
587                         }, "#A1-4");
588
589                         cip = new CacheItemPolicy ();
590                         cip.SlidingExpiration = TimeSpan.MinValue;
591                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
592                                 mc.Set ("key", "value", cip);
593                         }, "#A1-5");
594
595                         cip = new CacheItemPolicy ();
596                         cip.SlidingExpiration = TimeSpan.FromTicks (0L);
597                         mc.Set ("key_A1-6", "value", cip);
598                         Assert.IsTrue (mc.Contains ("key_A1-6"), "#A1-6");
599
600                         cip = new CacheItemPolicy ();
601                         cip.SlidingExpiration = TimeSpan.FromDays (500);
602                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
603                                 mc.Set ("key", "value", cip);
604                         }, "#A1-7");
605                         
606                         cip = new CacheItemPolicy ();
607                         cip.SlidingExpiration = TimeSpan.FromDays (365);
608                         mc.Set ("key_A1-8", "value", cip);
609                         Assert.IsTrue (mc.Contains ("key_A1-8"), "#A1-8");
610
611                         cip = new CacheItemPolicy ();
612                         cip.Priority = (CacheItemPriority) 20;
613                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
614                                 mc.Set ("key", "value", cip);
615                         }, "#A1-9");
616                         
617                         cip = new CacheItemPolicy ();
618                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
619                         mc.Set ("key_A2", "value_A2", cip);
620                         Assert.IsTrue (mc.Contains ("key_A2"), "#A2");
621
622                         mc.Set ("key_A3", "value_A3", new CacheItemPolicy ());
623                         Assert.IsTrue (mc.Contains ("key_A3"), "#A3-1");
624                         Assert.AreEqual ("value_A3", mc.Get ("key_A3"), "#A3-2");
625
626                         // The entry is never inserted as its expiration date is before now
627                         cip = new CacheItemPolicy ();
628                         cip.AbsoluteExpiration = DateTimeOffset.MinValue;
629                         mc.Set ("key_A4", "value_A4", cip);
630                         Assert.IsFalse (mc.Contains ("key_A4"), "#A4");
631
632                         mc.Calls.Clear ();
633                         cip = new CacheItemPolicy ();
634                         cip.AbsoluteExpiration = DateTimeOffset.MaxValue;
635                         mc.Set ("key_A5", "value_A5", cip);
636                         Assert.IsTrue (mc.Contains ("key_A5"), "#A5-1");
637                         Assert.AreEqual (2, mc.Calls.Count, "#A5-2");
638                         Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [0], "#A5-3");
639                 }
640
641                 [Test]
642                 public void Set_String_Object_DateTimeOffset_String ()
643                 {
644                         var mc = new PokerMemoryCache ("MyCache");
645
646                         AssertExtensions.Throws<NotSupportedException> (() => {
647                                 mc.Set ("key", "value", DateTimeOffset.MaxValue, "region");
648                         }, "#A1-1");
649
650                         AssertExtensions.Throws<ArgumentNullException> (() => {
651                                 mc.Set (null, "value", DateTimeOffset.MaxValue);
652                         }, "#A1-2");
653
654                         AssertExtensions.Throws<ArgumentNullException> (() => {
655                                 mc.Set ("key", null, DateTimeOffset.MaxValue);
656                         }, "#A1-3");
657                         
658                         // The entry is never inserted as its expiration date is before now
659                         mc.Set ("key_A2", "value_A2", DateTimeOffset.MinValue);
660                         Assert.IsFalse (mc.Contains ("key_A2"), "#A2");
661
662                         mc.Calls.Clear ();
663                         mc.Set ("key", "value", DateTimeOffset.MaxValue);
664
665                         Assert.AreEqual (2, mc.Calls.Count, "#A2-1");
666                         Assert.AreEqual ("Set (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls [0], "#A2-2");
667                         Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [1], "#A2-3");
668                 }
669
670                 [Test]
671                 public void Set_CacheItem_CacheItemPolicy ()
672                 {
673                         var mc = new PokerMemoryCache ("MyCache");
674
675                         AssertExtensions.Throws<ArgumentNullException> (() => {
676                                 mc.Set (null, new CacheItemPolicy ());
677                         }, "#A1-1");
678
679                         // Actually thrown from the Set (string, object, CacheItemPolicy, string) overload
680                         var ci = new CacheItem (null, "value");
681                         AssertExtensions.Throws<ArgumentNullException> (() => {
682                                 mc.Set (ci, new CacheItemPolicy ());
683                         }, "#A1-2");
684
685                         ci = new CacheItem ("key", null);
686                         AssertExtensions.Throws<ArgumentNullException> (() => {
687                                 mc.Set (ci, new CacheItemPolicy ());
688                         }, "#A1-3");
689
690                         ci = new CacheItem ("key", "value");
691                         var cip = new CacheItemPolicy ();
692                         cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { };
693                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
694                         AssertExtensions.Throws<ArgumentException> (() => {
695                                 mc.Set (ci, cip);
696                         }, "#A1-4");
697
698                         ci = new CacheItem ("key", "value");
699                         cip = new CacheItemPolicy ();
700                         cip.SlidingExpiration = TimeSpan.MinValue;
701                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
702                                 mc.Set (ci, cip);
703                         }, "#A1-5");
704
705                         ci = new CacheItem ("key_A1-6", "value");
706                         cip = new CacheItemPolicy ();
707                         cip.SlidingExpiration = TimeSpan.FromTicks (0L);
708                         mc.Set (ci, cip);
709                         Assert.IsTrue (mc.Contains ("key_A1-6"), "#A1-6");
710
711                         ci = new CacheItem ("key", "value");
712                         cip = new CacheItemPolicy ();
713                         cip.SlidingExpiration = TimeSpan.FromDays (500);
714                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
715                                 mc.Set (ci, cip);
716                         }, "#A1-7");
717
718                         ci = new CacheItem ("key_A1-8", "value");
719                         cip = new CacheItemPolicy ();
720                         cip.SlidingExpiration = TimeSpan.FromDays (365);
721                         mc.Set (ci, cip);
722                         Assert.IsTrue (mc.Contains ("key_A1-8"), "#A1-8");
723
724                         ci = new CacheItem ("key", "value");
725                         cip = new CacheItemPolicy ();
726                         cip.Priority = (CacheItemPriority) 20;
727                         AssertExtensions.Throws<ArgumentOutOfRangeException> (() => {
728                                 mc.Set (ci, cip);
729                         }, "#A1-9");
730
731                         ci = new CacheItem ("key_A2", "value_A2");
732                         cip = new CacheItemPolicy ();
733                         cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { };
734                         mc.Set (ci, cip);
735                         Assert.IsTrue (mc.Contains ("key_A2"), "#A2");
736
737                         ci = new CacheItem ("key_A3", "value_A3");
738                         mc.Set (ci, new CacheItemPolicy ());
739                         Assert.IsTrue (mc.Contains ("key_A3"), "#A3-1");
740                         Assert.AreEqual ("value_A3", mc.Get ("key_A3"), "#A3-2");
741
742                         // The entry is never inserted as its expiration date is before now
743                         ci = new CacheItem ("key_A4", "value");
744                         cip = new CacheItemPolicy ();
745                         cip.AbsoluteExpiration = DateTimeOffset.MinValue;
746                         mc.Set (ci, cip);
747                         Assert.IsFalse (mc.Contains ("key_A4"), "#A4");
748
749                         ci = new CacheItem ("key_A5", "value");
750                         mc.Calls.Clear ();
751                         mc.Set (ci, new CacheItemPolicy ());
752
753                         Assert.AreEqual (2, mc.Calls.Count, "#A5-1");
754                         Assert.AreEqual ("Set (CacheItem item, CacheItemPolicy policy)", mc.Calls [0], "#A5-2");
755                         Assert.AreEqual ("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls [1], "#A5-3");
756                 }
757
758                 [Test]
759                 public void Remove ()
760                 {
761                         var mc = new PokerMemoryCache ("MyCache");
762                 
763                         AssertExtensions.Throws<NotSupportedException> (() => {
764                                 mc.Remove ("key", "region");
765                         }, "#A1-1");
766
767                         AssertExtensions.Throws<ArgumentNullException> (() => {
768                                 mc.Remove (null);
769                         }, "#A1-2");
770
771                         bool callbackInvoked;
772                         CacheEntryRemovedReason reason = (CacheEntryRemovedReason) 1000;
773                         var cip = new CacheItemPolicy ();
774                         cip.Priority = CacheItemPriority.NotRemovable;
775                         mc.Set ("key2", "value1", cip);
776                         object value = mc.Remove ("key2");
777
778                         Assert.IsNotNull (value, "#B1-1");
779                         Assert.IsFalse (mc.Contains ("key2"), "#B1-2");
780
781                         cip = new CacheItemPolicy ();
782                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
783                                 callbackInvoked = true;
784                                 reason = args.RemovedReason;
785                         };
786
787                         mc.Set ("key", "value", cip);
788                         callbackInvoked = false;
789                         reason = (CacheEntryRemovedReason) 1000;
790                         value = mc.Remove ("key");
791                         Assert.IsNotNull (value, "#C1-1");
792                         Assert.IsTrue (callbackInvoked, "#C1-2");
793                         Assert.AreEqual (CacheEntryRemovedReason.Removed, reason, "#C1-3");
794
795                         cip = new CacheItemPolicy ();
796                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
797                                 callbackInvoked = true;
798                                 reason = args.RemovedReason;
799                                 throw new ApplicationException ("test");
800                         };
801
802                         mc.Set ("key", "value", cip);
803                         callbackInvoked = false;
804                         reason = (CacheEntryRemovedReason) 1000;
805                         value = mc.Remove ("key");
806                         Assert.IsNotNull (value, "#C2-1");
807                         Assert.IsTrue (callbackInvoked, "#C2-2");
808                         Assert.AreEqual (CacheEntryRemovedReason.Removed, reason, "#C2-3");
809
810                         // LAMESPEC: UpdateCallback is not called on remove
811                         cip = new CacheItemPolicy ();
812                         cip.UpdateCallback = (CacheEntryUpdateArguments args) => {
813                                 callbackInvoked = true;
814                                 reason = args.RemovedReason;
815                         };
816
817                         mc.Set ("key", "value", cip);
818                         callbackInvoked = false;
819                         reason = (CacheEntryRemovedReason) 1000;
820                         value = mc.Remove ("key");
821                         Assert.IsNotNull (value, "#D1-1");
822                         Assert.IsFalse (callbackInvoked, "#D1-2");
823
824                         cip = new CacheItemPolicy ();
825                         cip.UpdateCallback = (CacheEntryUpdateArguments args) => {
826                                 callbackInvoked = true;
827                                 reason = args.RemovedReason;
828                                 throw new ApplicationException ("test");
829                         };
830
831                         mc.Set ("key", "value", cip);
832                         callbackInvoked = false;
833                         reason = (CacheEntryRemovedReason) 1000;
834                         value = mc.Remove ("key");
835                         Assert.IsNotNull (value, "#D2-1");
836                         Assert.IsFalse (callbackInvoked, "#D2-2");
837                 }
838
839                 [Test]
840                 public void TimedExpiration ()
841                 {
842                         bool expired = false;
843                         CacheEntryRemovedReason reason = CacheEntryRemovedReason.CacheSpecificEviction;
844                         NameValueCollection config;
845                         int sleepPeriod;
846 #if !DOTNET
847                         config = new NameValueCollection ();
848                         config.Add ("__MonoTimerPeriod", "1");
849                         sleepPeriod = 1100;
850 #else
851                         config = null;
852                         sleepPeriod = 20100; // 20s is the .NET period - discovered by experimentation
853 #endif
854                         var mc = new PokerMemoryCache ("MyCache", config);
855                         var cip = new CacheItemPolicy ();
856
857                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
858                                 expired = true;
859                                 reason = args.RemovedReason;
860                         };
861                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
862                         mc.Set ("key", "value", cip);
863                         Thread.Sleep (100);
864
865                         Assert.IsFalse (expired, "#A1");
866                         object value = mc.Get ("key");
867
868                         Assert.IsNull (value, "#A2-1");
869                         Assert.IsTrue (expired, "#A2-2");
870                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "A2-3");
871
872                         expired = false;
873                         cip = new CacheItemPolicy ();
874                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
875                                 expired = true;
876                                 reason = args.RemovedReason;
877                         };
878                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
879                         mc.Set ("key", "value", cip);
880                         Thread.Sleep (sleepPeriod);
881
882                         Assert.IsTrue (expired, "#A3-1");
883                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#A3-2");
884
885                         int expiredCount = 0;
886                         object expiredCountLock = new object ();
887                         CacheEntryRemovedCallback removedCb = (CacheEntryRemovedArguments args) => {
888                                 lock (expiredCountLock) {
889                                         expiredCount++;
890                                 }
891                         };
892
893                         cip = new CacheItemPolicy ();
894                         cip.RemovedCallback = removedCb;
895                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (20);
896                         mc.Set ("key1", "value1", cip);
897
898                         cip = new CacheItemPolicy ();
899                         cip.RemovedCallback = removedCb;
900                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (200);
901                         mc.Set ("key2", "value2", cip);
902
903                         cip = new CacheItemPolicy ();
904                         cip.RemovedCallback = removedCb;
905                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (600);
906                         mc.Set ("key3", "value3", cip);
907
908                         cip = new CacheItemPolicy ();
909                         cip.RemovedCallback = removedCb;
910                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (sleepPeriod + 100);
911                         mc.Set ("key4", "value4", cip);
912                         
913                         Thread.Sleep (sleepPeriod);
914                         Assert.AreEqual (3, expiredCount, "#A4");
915                 }
916
917                 [Test]
918                 public void GetEnumerator ()
919                 {
920                         var mc = new PokerMemoryCache ("MyCache");
921
922                         // This one is a Hashtable enumerator
923                         IEnumerator enumerator = ((IEnumerable) mc).GetEnumerator ();
924
925                         // This one is a Dictionary <string, object> enumerator
926                         IEnumerator enumerator2 = mc.DoGetEnumerator ();
927
928                         Assert.IsNotNull (enumerator, "#A1-1");
929                         Assert.IsNotNull (enumerator2, "#A1-2");
930                         Assert.IsTrue (enumerator.GetType () != enumerator2.GetType (), "#A1-3");
931
932                         mc.Set ("key1", "value1", null);
933                         mc.Set ("key2", "value2", null);
934                         mc.Set ("key3", "value3", null);
935
936                         bool expired = false;
937                         var cip = new CacheItemPolicy ();
938                         cip.AbsoluteExpiration = DateTime.Now.AddMilliseconds (50);
939                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
940                                 expired = true;
941                         };
942
943                         mc.Set ("key4", "value4", cip);
944                         Thread.Sleep (100);
945
946                         enumerator = ((IEnumerable) mc).GetEnumerator ();
947                         int count = 0;
948                         while (enumerator.MoveNext ()) {
949                                 count++;
950                         }
951
952                         Assert.IsFalse (expired, "#A2-1");
953                         Assert.AreEqual (3, count, "#A2-2");
954
955                         expired = false;
956                         cip = new CacheItemPolicy ();
957                         cip.AbsoluteExpiration = DateTime.Now.AddMilliseconds (50);
958                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
959                                 expired = true;
960                         };
961
962                         mc.Set ("key5", "value5", cip);
963                         Thread.Sleep (100);
964
965                         enumerator2 = mc.DoGetEnumerator ();
966                         count = 0;
967                         while (enumerator2.MoveNext ()) {
968                                 count++;
969                         }
970
971                         Assert.IsFalse (expired, "#A3-1");
972                         Assert.AreEqual (3, count, "#A3-2");
973                 }
974
975                 [Test]
976                 public void GetValues ()
977                 {
978                         var mc = new PokerMemoryCache ("MyCache");
979
980                         AssertExtensions.Throws<ArgumentNullException> (() => {
981                                 mc.GetValues (null);
982                         }, "#A1-1");
983
984                         AssertExtensions.Throws<NotSupportedException> (() => {
985                                 mc.GetValues (new string[] {}, "region");
986                         }, "#A1-2");
987
988                         AssertExtensions.Throws<ArgumentException> (() => {
989                                 mc.GetValues (new string [] { "key", null });
990                         }, "#A1-3");
991
992                         IDictionary<string, object> value = mc.GetValues (new string[] {});
993                         Assert.IsNull (value, "#A2");
994
995                         mc.Set ("key1", "value1", null);
996                         mc.Set ("key2", "value2", null);
997                         mc.Set ("key3", "value3", null);
998
999                         Assert.IsTrue (mc.Contains ("key1"), "#A3-1");
1000                         Assert.IsTrue (mc.Contains ("key2"), "#A3-2");
1001                         Assert.IsTrue (mc.Contains ("key3"), "#A3-2");
1002
1003                         value = mc.GetValues (new string [] { "key1", "key3" });
1004                         Assert.IsNotNull (value, "#A4-1");
1005                         Assert.AreEqual (2, value.Count, "#A4-2");
1006                         Assert.AreEqual ("value1", value ["key1"], "#A4-3");
1007                         Assert.AreEqual ("value3", value ["key3"], "#A4-4");
1008                         Assert.AreEqual (typeof (Dictionary<string, object>), value.GetType (), "#A4-5");
1009
1010                         // LAMESPEC: MSDN says the number of items in the returned dictionary should be the same as in the 
1011                         // 'keys' collection - this is not the case. The returned dictionary contains only entries for keys
1012                         // that exist in the cache.
1013                         value = mc.GetValues (new string [] { "key1", "key3", "nosuchkey" });
1014                         Assert.IsNotNull (value, "#A5-1");
1015                         Assert.AreEqual (2, value.Count, "#A5-2");
1016                         Assert.AreEqual ("value1", value ["key1"], "#A5-3");
1017                         Assert.AreEqual ("value3", value ["key3"], "#A5-4");
1018                         Assert.IsFalse (value.ContainsKey ("Key1"), "#A5-5");
1019                 }
1020
1021                 [Test]
1022                 public void Get ()
1023                 {
1024                         var mc = new PokerMemoryCache ("MyCache");
1025
1026                         AssertExtensions.Throws<NotSupportedException> (() => {
1027                                 mc.Get ("key", "region");
1028                         }, "#A1-1");
1029
1030                         AssertExtensions.Throws<ArgumentNullException> (() => {
1031                                 mc.Get (null);
1032                         }, "#A1-2");
1033
1034                         object value;
1035                         mc.Set ("key", "value", null);
1036                         value = mc.Get ("key");
1037                         Assert.IsNotNull (value, "#A2-1");
1038                         Assert.AreEqual ("value", value, "#A2-2");
1039
1040                         value = mc.Get ("nosuchkey");
1041                         Assert.IsNull (value, "#A3");
1042
1043                         var cip = new CacheItemPolicy ();
1044                         bool callbackInvoked;
1045                         CacheEntryRemovedReason reason = (CacheEntryRemovedReason)1000;
1046
1047                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1048                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1049                                 callbackInvoked = true;
1050                                 reason = args.RemovedReason;
1051                         };
1052                         mc.Set ("key", "value", cip);
1053                         Thread.Sleep (500);
1054
1055                         callbackInvoked = false;
1056                         reason = (CacheEntryRemovedReason) 1000;
1057                         value = mc.Get ("key");
1058                         Assert.IsNull (value, "#B1-1");
1059                         Assert.IsTrue (callbackInvoked, "#B1-2");
1060                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B1-3");
1061
1062                         cip = new CacheItemPolicy ();
1063                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1064                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1065                                 callbackInvoked = true;
1066                                 reason = args.RemovedReason;
1067                                 throw new ApplicationException ("test");
1068                         };
1069
1070                         mc.Set ("key", "value", cip);
1071                         Thread.Sleep (500);
1072
1073                         callbackInvoked = false;
1074                         reason = (CacheEntryRemovedReason) 1000;
1075                         value = mc.Get ("key");
1076                         Assert.IsNull (value, "#B2-1");
1077                         Assert.IsTrue (callbackInvoked, "#B2-2");
1078                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B2-3");
1079                 }
1080
1081                 [Test]
1082                 public void GetCacheItem ()
1083                 {
1084                         var mc = new PokerMemoryCache ("MyCache");
1085
1086                         AssertExtensions.Throws<NotSupportedException> (() => {
1087                                 mc.GetCacheItem ("key", "region");
1088                         }, "#A1-1");
1089
1090                         AssertExtensions.Throws<ArgumentNullException> (() => {
1091                                 mc.GetCacheItem (null);
1092                         }, "#A1-2");
1093
1094                         CacheItem value;
1095                         mc.Set ("key", "value", null);
1096                         value = mc.GetCacheItem ("key");
1097                         Assert.IsNotNull (value, "#A2-1");
1098                         Assert.AreEqual ("value", value.Value, "#A2-2");
1099                         Assert.AreEqual ("key", value.Key, "#A2-3");
1100
1101                         value = mc.GetCacheItem ("doesnotexist");
1102                         Assert.IsNull (value, "#A3");
1103
1104                         var cip = new CacheItemPolicy ();
1105                         bool callbackInvoked;
1106                         CacheEntryRemovedReason reason = (CacheEntryRemovedReason) 1000;
1107
1108                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1109                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1110                                 callbackInvoked = true;
1111                                 reason = args.RemovedReason;
1112                         };
1113                         mc.Set ("key", "value", cip);
1114                         Thread.Sleep (500);
1115
1116                         callbackInvoked = false;
1117                         reason = (CacheEntryRemovedReason) 1000;
1118                         value = mc.GetCacheItem ("key");
1119                         Assert.IsNull (value, "#B1-1");
1120                         Assert.IsTrue (callbackInvoked, "#B1-2");
1121                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B1-3");
1122
1123                         cip = new CacheItemPolicy ();
1124                         cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds (50);
1125                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1126                                 callbackInvoked = true;
1127                                 reason = args.RemovedReason;
1128                                 throw new ApplicationException ("test");
1129                         };
1130
1131                         mc.Set ("key", "value", cip);
1132                         Thread.Sleep (500);
1133
1134                         callbackInvoked = false;
1135                         reason = (CacheEntryRemovedReason) 1000;
1136                         value = mc.GetCacheItem ("key");
1137                         Assert.IsNull (value, "#B2-1");
1138                         Assert.IsTrue (callbackInvoked, "#B2-2");
1139                         Assert.AreEqual (CacheEntryRemovedReason.Expired, reason, "#B2-3");
1140                 }
1141
1142                 [Test]
1143                 public void ChangeMonitors ()
1144                 {
1145                         bool removed = false;
1146                         var mc = new PokerMemoryCache ("MyCache");
1147                         var cip = new CacheItemPolicy ();
1148                         var monitor = new PokerChangeMonitor ();
1149                         cip.ChangeMonitors.Add (monitor);
1150                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1151                                 removed = true;
1152                         };
1153
1154                         mc.Set ("key", "value", cip);
1155                         Assert.AreEqual (0, monitor.Calls.Count, "#A1");
1156
1157                         monitor.SignalChange ();
1158                         Assert.IsTrue (removed, "#A2");
1159
1160                         bool onChangedCalled = false;
1161                         monitor = new PokerChangeMonitor ();
1162                         monitor.NotifyOnChanged ((object state) => {
1163                                 onChangedCalled = true;
1164                         });
1165
1166                         cip = new CacheItemPolicy ();
1167                         cip.ChangeMonitors.Add (monitor);
1168
1169                         // Thrown by ChangeMonitor.NotifyOnChanged
1170                         AssertExtensions.Throws<InvalidOperationException> (() => {
1171                                 mc.Set ("key1", "value1", cip);
1172                         }, "#A3");
1173                 }
1174
1175                 // NOTE: on Windows with 2 or more CPUs this test will most probably fail.
1176                 [Test]
1177                 public void Trim ()
1178                 {
1179                         var config = new NameValueCollection ();
1180                         config ["__MonoEmulateOneCPU"] = "true";
1181                         var mc = new MemoryCache ("MyCache", config);
1182
1183                         for (int i = 0; i < 10; i++)
1184                                 mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1185
1186                         // .NET doesn't touch the freshest 10 entries
1187                         Assert.AreEqual (10, mc.GetCount (), "#A1-1");
1188                         long trimmed = mc.Trim (50);
1189                         Assert.AreEqual (0, trimmed, "#A1-2");
1190                         Assert.AreEqual (10, mc.GetCount (), "#A1-3");
1191
1192                         mc = new MemoryCache ("MyCache", config);
1193                         // Only entries 11- are considered for removal
1194                         for (int i = 0; i < 11; i++)
1195                                 mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1196
1197                         Assert.AreEqual (11, mc.GetCount (), "#A2-1");
1198                         trimmed = mc.Trim (50);
1199                         Assert.AreEqual (1, trimmed, "#A2-2");
1200                         Assert.AreEqual (10, mc.GetCount (), "#A2-3");
1201
1202                         mc = new MemoryCache ("MyCache", config);
1203                         // Only entries 11- are considered for removal
1204                         for (int i = 0; i < 125; i++)
1205                                 mc.Set ("key" + i.ToString (), "value" + i.ToString (), null);
1206
1207                         Assert.AreEqual (125, mc.GetCount (), "#A3-1");
1208                         trimmed = mc.Trim (50);
1209                         Assert.AreEqual (62, trimmed, "#A3-2");
1210                         Assert.AreEqual (63, mc.GetCount (), "#A3-3");
1211
1212                         // Testing the removal order
1213                         mc = new MemoryCache ("MyCache", config);
1214                         var removed = new List <string> ();
1215                         var cip = new CacheItemPolicy ();
1216                         cip.RemovedCallback = (CacheEntryRemovedArguments args) => {
1217                                 removed.Add (args.CacheItem.Key);
1218                         };
1219
1220                         for (int i = 0; i < 50; i++)
1221                                 mc.Set ("key" + i.ToString (), "value" + i.ToString (), cip);
1222
1223                         object value;
1224                         for (int i = 0; i < 50; i++)
1225                                 value = mc.Get ("key" + i.ToString ());
1226
1227                         trimmed = mc.Trim (50);
1228                         Assert.AreEqual (25, mc.GetCount (), "#A4-1");
1229                         Assert.AreEqual (25, trimmed, "#A4-2");
1230                         Assert.AreEqual (25, removed.Count, "#A4-3");
1231
1232                         // OK, this is odd... The list is correct in terms of entries removed but the entries
1233                         // are removed in the _MOST_ frequently used order, within the group selected for removal.
1234                         for (int i = 24; i >= 0; i--) {
1235                                 int idx = 24 - i;
1236                                 Assert.AreEqual ("key" + i.ToString (), removed [idx], "#A5-" + idx.ToString ());
1237                         }
1238                 }
1239         }
1240 }