public void GetDoDelayOperationsEnumeratorPasses()
        {
            var root = new ModelClass()
            {
                Name = "root"
            };
            var apple = new ModelClass()
            {
                Name = "apple"
            };
            var grape = new ModelClass()
            {
                Name = "grape"
            };

            root.AddChildren(apple, grape);
            var orange = new ModelClass()
            {
                Name = "orange"
            };

            grape.AddChildren(orange);

            var allBinder = new ModelViewBinder("*", null, new ModelViewBinder.BindInfo(typeof(IntViewObjClass)));

            var viewInstanceCreator = new DefaultViewInstanceCreator(
                (typeof(IntViewObjClass), new IntViewObjClass.Binder()),
                (typeof(FloatViewObjClass), new FloatViewObjClass.Binder())
                );
            var binderMap       = new ModelViewBinderMap(viewInstanceCreator, allBinder);
            var bindInstanceMap = new ModelViewBinderInstanceMap(binderMap);

            Assert.IsFalse(bindInstanceMap.EnabledDelayOperation);
            bindInstanceMap.EnabledDelayOperation = true;

            {//操作は対象になっているIModel分だけ生成されるようにする
                bindInstanceMap.Add(false, null, root.GetHierarchyEnumerable());
                bindInstanceMap.Remove(root.GetHierarchyEnumerable());

                var correctOpCount = root.GetHierarchyEnumerable().Count();
                var enumerator     = bindInstanceMap.GetDoDelayOperationsEnumerator();

                var opCount = 0;
                while (enumerator.MoveNext() && enumerator.Current != null)
                {
                    opCount++;
                }

                Assert.AreEqual(correctOpCount, opCount, "登録された操作の個数が想定されたものになっていません。操作対象となっているIModelの数だけ操作を生成してくください。");

                bindInstanceMap.Add(false, null, root.GetHierarchyEnumerable());
                correctOpCount = root.GetHierarchyEnumerable().Count();
                opCount        = 0;
                while (enumerator.MoveNext() && enumerator.Current != null)
                {
                    opCount++;
                }
                Assert.AreEqual(correctOpCount, opCount, "再び操作が追加された後でも、ModelViewBinderInstanceMap#GetDoDelayOperationsEnumerator関数の返り値のIEnumeratorが使用できるようにしてください。");
            }
        }
        public void BasicUsagePasses()
        {
            var root = new ModelClass()
            {
                Name = "root"
            };
            var apple = new ModelClass()
            {
                Name = "apple"
            };
            var grape = new ModelClass()
            {
                Name = "grape"
            };

            root.AddChildren(apple, grape);
            var orange = new ModelClass()
            {
                Name = "orange"
            };

            grape.AddChildren(orange);

            var appleBinder = new ModelViewBinder("apple", null,
                                                  new ModelViewBinder.BindInfo(typeof(IntViewObjClass)));
            var orangeBinder = new ModelViewBinder("orange", null,
                                                   new ModelViewBinder.BindInfo(typeof(FloatViewObjClass)));
            var rebindBinder = new ModelViewBinder("Rebind", null,
                                                   new ModelViewBinder.BindInfo(typeof(FloatViewObjClass)));

            Assert.IsFalse(root.DoMatchQueryPath(appleBinder.Query));
            var viewInstanceCreator = new DefaultViewInstanceCreator(
                (typeof(IntViewObjClass), new IntViewObjClass.Binder()),
                (typeof(FloatViewObjClass), new FloatViewObjClass.Binder())
                );
            var binderMap = new ModelViewBinderMap(viewInstanceCreator, appleBinder, orangeBinder, rebindBinder);
            {//Constructorのテスト
                Assert.AreEqual(3, binderMap.Binders.Count());
                Assert.IsTrue(binderMap.Binders.Any(_b => _b == appleBinder), "指定したBinderがBinderMapの中にありません");
                Assert.IsTrue(binderMap.Binders.Any(_b => _b == orangeBinder), "指定したBinderがBinderMapの中にありません");
                Assert.IsTrue(binderMap.Binders.Any(_b => _b == rebindBinder), "指定したBinderがBinderMapの中にありません");

                Assert.AreSame(viewInstanceCreator, binderMap.ViewInstanceCreator);
                string errorMessage = "ModelViewBinderMapに設定されたModelViewBinder#ViewInstaceCreatorはModelViewBinderMap#ViewInstanceCreatorと同じものになるようにしてください。";
                Assert.AreSame(binderMap.ViewInstanceCreator, appleBinder.ViewInstaceCreator, errorMessage);
                Assert.AreSame(binderMap.ViewInstanceCreator, orangeBinder.ViewInstaceCreator, errorMessage);
                Assert.AreSame(binderMap.ViewInstanceCreator, rebindBinder.ViewInstaceCreator, errorMessage);
            }

            {//ModelViewBindMap#CreateBindInstanceのテスト
                var appleBindInstance  = binderMap.CreateBindInstance(apple, null);
                var orangeBindInstance = binderMap.CreateBindInstance(orange, null);
                Assert.IsNotNull(appleBindInstance);
                Assert.IsNotNull(orangeBindInstance);
                Assert.IsNull(binderMap.CreateBindInstance(root, null));
                Assert.IsNull(binderMap.CreateBindInstance(grape, null));

                Assert.AreSame(apple, appleBindInstance.Model);
                Assert.AreSame(appleBinder, appleBindInstance.Binder);
                Assert.AreEqual(1, appleBindInstance.ViewObjects.Count());
                Assert.AreEqual(typeof(IntViewObjClass), appleBindInstance.ViewObjects.First().GetType());

                Assert.AreSame(orange, orangeBindInstance.Model);
                Assert.AreSame(orangeBinder, orangeBindInstance.Binder);
                Assert.AreEqual(1, orangeBindInstance.ViewObjects.Count());
                Assert.AreEqual(typeof(FloatViewObjClass), orangeBindInstance.ViewObjects.First().GetType());
            }
        }
        public void QueryPathPriorityPasses()
        {
            var root = new ModelClass()
            {
                Name = "root"
            };
            var apple = new ModelClass()
            {
                Name = "apple"
            };
            var grape = new ModelClass()
            {
                Name = "grape"
            };

            root.AddChildren(apple, grape);
            var orange = new ModelClass()
            {
                Name = "orange", LogicalID = new ModelIDList("child")
            };

            grape.AddChildren(orange);

            var allBinder = new ModelViewBinder("*", null,
                                                new ModelViewBinder.BindInfo(typeof(IntViewObjClass)));
            var appleBinder = new ModelViewBinder("apple", null,
                                                  new ModelViewBinder.BindInfo(typeof(IntViewObjClass)));
            var orangeBinder = new ModelViewBinder("orange", null,
                                                   new ModelViewBinder.BindInfo(typeof(FloatViewObjClass)));
            var childOrangeBinder = new ModelViewBinder("orange #child", null,
                                                        new ModelViewBinder.BindInfo(typeof(FloatViewObjClass)));

            Assert.IsFalse(root.DoMatchQueryPath(appleBinder.Query));

            var viewInstanceCreator = new DefaultViewInstanceCreator(
                (typeof(IntViewObjClass), new IntViewObjClass.Binder()),
                (typeof(FloatViewObjClass), new FloatViewObjClass.Binder())
                );
            var binderMap = new ModelViewBinderMap(
                viewInstanceCreator,
                allBinder,
                appleBinder,
                orangeBinder,
                childOrangeBinder);
            var bindInstanceMap = binderMap.CreateBinderInstaceMap();

            bindInstanceMap.Add(false, null, root.GetHierarchyEnumerable());

            var rootBinderInstance = bindInstanceMap.BindInstances[root];

            Assert.AreSame(allBinder, rootBinderInstance.Binder);

            var appleBinderInstance = bindInstanceMap.BindInstances[apple];

            Assert.AreSame(appleBinder, appleBinderInstance.Binder);

            // grape/orangeのものを使用すること
            var orangeBinderInstance = bindInstanceMap.BindInstances[orange];

            Assert.AreSame(childOrangeBinder, orangeBinderInstance.Binder);

            var grapeBinderInstance = bindInstanceMap.BindInstances[grape];

            Assert.AreSame(allBinder, grapeBinderInstance.Binder);
        }
        public void DelayOperationPasses()
        {
            var root = new ModelClass()
            {
                Name = "root"
            };
            var apple = new ModelClass()
            {
                Name = "apple"
            };
            var grape = new ModelClass()
            {
                Name = "grape"
            };

            root.AddChildren(apple, grape);
            var orange = new ModelClass()
            {
                Name = "orange"
            };

            grape.AddChildren(orange);

            var allBinder = new ModelViewBinder("*", null,
                                                new ModelViewBinder.BindInfo(typeof(IntViewObjClass)));

            var viewInstanceCreator = new DefaultViewInstanceCreator(
                (typeof(IntViewObjClass), new IntViewObjClass.Binder()),
                (typeof(FloatViewObjClass), new FloatViewObjClass.Binder())
                );
            var binderMap       = new ModelViewBinderMap(viewInstanceCreator, allBinder);
            var bindInstanceMap = new ModelViewBinderInstanceMap(binderMap);

            Assert.IsFalse(bindInstanceMap.EnabledDelayOperation);
            bindInstanceMap.EnabledDelayOperation = true;

            {//追加
                bindInstanceMap.Add(false, null, root.GetHierarchyEnumerable());
                Assert.IsFalse(bindInstanceMap.BindInstances.Any(), "遅延操作が有効になっている場合はBindInstanceMap#DoDelayOperation()を呼び出されるまで、追加処理を実行しないでください。");

                bindInstanceMap.DoDelayOperations();
                Assert.IsTrue(bindInstanceMap.BindInstances.Any());

                {//一度操作が実行された後は同じ操作を繰り返し実行されないようにする
                    var e       = bindInstanceMap.GetDoDelayOperationsEnumerator();
                    var opCount = 0;
                    while (e.MoveNext() && e.Current != null)
                    {
                        opCount++;
                    }
                    Assert.AreEqual(0, opCount, "一度操作を実行した後はそれを削除するようにしてください");
                }
            }

            {     //更新 Model#OnUpdatedが呼び出されるかどうかでテストしている
                var viewObj = bindInstanceMap.BindInstances[root].ViewObjects.First() as IntViewObjClass;
                { //ModelViewBindInstanceMap#UpdateViewObjects()
                    root.IntValue = 9874;
                    bindInstanceMap.UpdateViewObjects();
                    Assert.AreNotEqual(viewObj.IntValue, root.IntValue, $"遅延操作が有効な時は、ModelViewBindInstanceMap#DoDelayOperation()が呼び出されるまで更新処理を実行しないでください。");

                    bindInstanceMap.DoDelayOperations();
                    Assert.AreEqual(viewObj.IntValue, root.IntValue, $"遅延操作が更新に対応していません。");
                }

                {//Model#DoneUpdate()
                    root.IntValue++;
                    root.DoneUpdate();
                    Assert.AreNotEqual(viewObj.IntValue, root.IntValue, $"遅延操作が有効な時は、ModelViewBindInstanceMap#DoDelayOperation()が呼び出されるまで更新処理を実行しないでください。");

                    bindInstanceMap.DoDelayOperations();
                    Assert.AreEqual(viewObj.IntValue, root.IntValue, $"遅延操作が更新に対応していません。");
                }
            }

            {//削除
                var instanceCount = bindInstanceMap.BindInstances.Count();
                bindInstanceMap.Remove(bindInstanceMap.BindInstances.Keys);
                Assert.AreEqual(instanceCount, bindInstanceMap.BindInstances.Count()
                                , "遅延操作が有効になっている場合はBindInstanceMap#DoDelayOperation()を呼び出されるまで、削除処理を実行しないでください。");

                bindInstanceMap.DoDelayOperations();
                Assert.IsFalse(bindInstanceMap.BindInstances.Any());
            }

            {//追加と削除が合わせて遅延処理に登録されていたら、何もしないようにする
                bindInstanceMap.Add(false, null, root.GetHierarchyEnumerable());
                bindInstanceMap.Remove(root.GetHierarchyEnumerable());
                Assert.IsFalse(bindInstanceMap.BindInstances.Any(), "遅延操作が有効になっている場合はBindInstanceMap#DoDelayOperation()を呼び出されるまで、追加・削除処理を実行しないでください。");

                bindInstanceMap.DoDelayOperations();
                Assert.IsFalse(bindInstanceMap.BindInstances.Any(), "同じModelの追加・削除を遅延処理で同時に実行される場合は何もしないようにしてください");
            }
        }
        public void BinderInstanceMapBasicUsagePasses()
        {
            var root = new ModelClass()
            {
                Name = "root"
            };
            var apple = new ModelClass()
            {
                Name = "apple"
            };
            var noneBinderModel = new ModelClass()
            {
                Name = "grape"
            };

            root.AddChildren(apple, noneBinderModel);
            var orange = new ModelClass()
            {
                Name = "orange"
            };

            noneBinderModel.AddChildren(orange);

            var appleBinder = new ModelViewBinder("apple", null,
                                                  new ModelViewBinder.BindInfo(typeof(IntViewObjClass)));
            var orangeBinder = new ModelViewBinder("orange", null,
                                                   new ModelViewBinder.BindInfo(typeof(FloatViewObjClass)));
            var rebindBinder = new ModelViewBinder("Rebind", null,
                                                   new ModelViewBinder.BindInfo(typeof(FloatViewObjClass)));

            Assert.IsFalse(root.DoMatchQueryPath(appleBinder.Query));
            var viewInstanceCreator = new DefaultViewInstanceCreator(
                (typeof(IntViewObjClass), new IntViewObjClass.Binder()),
                (typeof(FloatViewObjClass), new FloatViewObjClass.Binder())
                );
            var binderMap = new ModelViewBinderMap(viewInstanceCreator, appleBinder, orangeBinder, rebindBinder);

            {//BinderInstanceMapのテスト
                var bindInstanceMap = new ModelViewBinderInstanceMap(binderMap);
                Assert.AreSame(binderMap, bindInstanceMap.BinderMap);

                {//BindInstanceMap#Addのテスト
                    Assert.AreEqual(0, bindInstanceMap.BindInstances.Count());
                    bindInstanceMap.Add(false, null, apple, noneBinderModel);
                    //grapeはQueryPathと一致しないので追加されない
                    AssertionUtils.AssertEnumerableByUnordered(new Model[] {
                        apple, orange
                    }, bindInstanceMap.BindInstances.Select(_b => _b.Key), "");

                    //追加された時は合わせてViewのパラメータもModelのものに更新する
                    var appleViewObj = bindInstanceMap[apple].ViewObjects.First(_v => _v is IntViewObjClass) as IntViewObjClass;
                    Assert.AreEqual(apple.IntValue, appleViewObj.IntValue);
                    foreach (var viewObj in bindInstanceMap[apple].ViewObjects)
                    {
                        Assert.AreEqual(bindInstanceMap[apple], viewObj.UseBinderInstance);
                    }
                    Assert.AreSame(bindInstanceMap, bindInstanceMap[apple].UseInstanceMap);

                    var orangeViewObj = bindInstanceMap[orange].ViewObjects.First(_v => _v is FloatViewObjClass) as FloatViewObjClass;
                    Assert.AreEqual(orange.FloatValue, orangeViewObj.FloatValue);
                    foreach (var viewObj in bindInstanceMap[orange].ViewObjects)
                    {
                        Assert.AreEqual(bindInstanceMap[orange], viewObj.UseBinderInstance);
                    }

                    //既に追加されていたら追加しない
                    bindInstanceMap.Add(false, null, apple, orange);
                    AssertionUtils.AssertEnumerableByUnordered(new Model[] {
                        apple, orange
                    }, bindInstanceMap.BindInstances.Select(_b => _b.Key), "同じModelが追加できないようにしてください");
                }

                {//マッチしないModelを追加した時のテスト
                    var empty = new Model()
                    {
                        Name = "__empty"
                    };
                    Assert.DoesNotThrow(() =>
                    {
                        bindInstanceMap.Add(empty);
                        Assert.IsFalse(bindInstanceMap.BindInstances.ContainsKey(empty), "マッチしないModelを追加した時はModelViewBinderInstanceを生成しないようにしてください");
                    }, "マッチしないModelを追加した時でもModelViewBinderInstanceMapから例外を発生させないようにしてください。");
                }

                var appleBindInstance  = bindInstanceMap[apple];
                var orangeBindInstance = bindInstanceMap[orange];
                {//BindInstanceMap#[]のテスト
                    Assert.IsNotNull(appleBindInstance);
                    Assert.AreEqual(apple, appleBindInstance.Model);

                    Assert.IsNotNull(orangeBindInstance);
                    Assert.AreEqual(orange, orangeBindInstance.Model);
                }

                {//BindInstanceMap#UpdateViewObjectsのテスト
                    apple.IntValue    = 234;
                    orange.FloatValue = 2.5432f;
                    bindInstanceMap.UpdateViewObjects();
                    var appleViewObj = appleBindInstance.ViewObjects.First(_v => _v is IntViewObjClass) as IntViewObjClass;
                    Assert.AreEqual(apple.IntValue, appleViewObj.IntValue);
                    var orangeViewObj = orangeBindInstance.ViewObjects.First(_v => _v is FloatViewObjClass) as FloatViewObjClass;
                    Assert.AreEqual(orange.FloatValue, orangeViewObj.FloatValue);
                }

                {//BindInstanceMap#Rebindのテスト
                    apple.Name = "Rebind";
                    var isSuccess = bindInstanceMap.Rebind(apple);
                    Assert.IsTrue(isSuccess, "Rebindに失敗しています");
                    Assert.AreSame(rebindBinder, bindInstanceMap.BindInstances[apple].Binder);
                    Assert.AreSame(bindInstanceMap, bindInstanceMap.BindInstances[apple].UseInstanceMap);
                }
                {
                    // 追加されていないものをRebindした時は何もしない
                    var recordedBindInstances = bindInstanceMap.BindInstances.ToArray();
                    var model = new ModelClass()
                    {
                        Name = "Tmp"
                    };
                    var isSuccess = bindInstanceMap.Rebind(model);
                    Assert.IsFalse(isSuccess, "登録されていないModelの場合はRebindしないようにしてください");
                    AssertionUtils.AssertEnumerable(recordedBindInstances, bindInstanceMap.BindInstances, "ModelViewBinderInstanceMapに追加されていないModelをRebindした時は何もしないようにしてください。");
                }

                {//BindInstanceMap#Removeのテスト
                    bindInstanceMap.Remove(apple, noneBinderModel);
                    Assert.AreEqual(0, bindInstanceMap.BindInstances.Count());
                }

                {//BindInstanceMap#ClearBindInstancesのテスト
                    bindInstanceMap.Add(false, null, apple, orange);
                    bindInstanceMap.ClearBindInstances();
                    Assert.AreEqual(0, bindInstanceMap.BindInstances.Count());
                }
            }
        }