public void UserInfo_add_remove_resources() {
     UserInfo userInfo = new UserInfo(1, "wicked");
     int resourcesChanged = 0;
     userInfo.ResourcesChanged += delegate { resourcesChanged++; };
     userInfo.AddResource(1, "0");
     Assert.AreEqual(1, resourcesChanged);
     Assert.AreEqual(1, userInfo.Resources.Length);
     Assert.AreEqual(1, userInfo.Resources[0].Item1);
     userInfo.AddResource(2, "0");
     userInfo.AddResource(3, "0");
     userInfo.AddResource(4, "0");
     Assert.AreEqual(4, resourcesChanged);
     Assert.AreEqual(4, userInfo.Resources.Length);
     List<uint> resources = new List<uint>();
     foreach(Tuplet<uint, string> tuple in userInfo.Resources) {
         resources.Add(tuple.Item1);
     }
     Assert.IsTrue(resources.Contains(2));
     userInfo.RemoveResource(2);
     Assert.AreEqual(5, resourcesChanged);
     Assert.AreEqual(3, userInfo.Resources.Length);
     resources.Clear();
     foreach(Tuplet<uint, string> tuple in userInfo.Resources) {
         resources.Add(tuple.Item1);
     }
     Assert.IsFalse(resources.Contains(2));
 }
 public void UserInfo_self_invalidates_on_expire_time() {
     TimeSpan defaultExpire = UserInfo.ValidationExpireTime;
     try {
         UserInfo.ValidationExpireTime = TimeSpan.FromMilliseconds(200);
         UserInfo userInfo = new UserInfo(1, "wicked");
         Assert.IsFalse(userInfo.IsValidated);
         userInfo.Save();
         Assert.IsTrue(userInfo.IsValidated);
         Thread.Sleep(1000);
         Assert.IsFalse(userInfo.IsValidated);
     } finally {
         UserInfo.ValidationExpireTime = defaultExpire;
     }
 }
 //--- Constructors ---
 public RecordEventArgs(string wikiId, UserInfo user, bool delete) {
     WikiId = wikiId;
     User = user;
     Delete = delete;
 }
        private void RecordsChange_Helper(UserInfo userInfo) {
            SiteInfo siteInfo;
            lock(_subscriptions) {
                if(!_subscriptions.TryGetValue(userInfo.WikiId, out siteInfo)) {
                    return;
                }
            }
            bool delete = false;
            lock(siteInfo) {
                if(userInfo.Resources.Length == 0 && siteInfo.Users.ContainsKey(userInfo.Id)) {

                    // user has no subscribed resources, remove
                    _log.DebugFormat("purging user '{0}', no subscriptions left", userInfo.Id);
                    siteInfo.Users.Remove(userInfo.Id);
                    delete = true;
                    userInfo.ResourcesChanged -= OnSubscriptionChange;
                    userInfo.DataChanged -= OnRecordsChange;
                }
            }
            if(RecordsChanged != null) {
                _log.Debug("firing RecordsChanged");
                RecordsChanged(this, new RecordEventArgs(userInfo.WikiId, userInfo, delete));
            }
        }
 //--- Methods ---
 public UserInfo GetUser(string wikiId, uint userId, bool create) {
     SiteInfo siteInfo;
     UserInfo userInfo;
     lock(_subscriptions) {
         if(_subscriptions.TryGetValue(wikiId, out siteInfo)) {
             lock(siteInfo) {
                 if(siteInfo.Users.TryGetValue(userId, out userInfo)) {
                     return userInfo;
                 }
             }
         }
         if(!create) {
             return null;
         }
         if(siteInfo == null) {
             siteInfo = new SiteInfo(wikiId);
             _subscriptions.Add(wikiId, siteInfo);
         }
         userInfo = new UserInfo(userId, wikiId);
         lock(siteInfo) {
             siteInfo.Users.Add(userId, userInfo);
         }
         userInfo.ResourcesChanged += OnSubscriptionChange;
         userInfo.DataChanged += OnRecordsChange;
         return userInfo;
     }
 }