Example #1
0
        public void NestedCircularReferenceWithPointerUpdateTest()
        {
            var initial = new TestPerson()
            {
                FirstName  = "John",
                LastName   = "Doe",
                IsAdult    = true,
                FirstChild = new TestPerson()
                {
                    FirstName = "Baby",
                    LastName  = "Doe",
                    IsAdult   = false,
                },
            };

            ITestPerson person = ITestPerson.Produce(initial);

            var result = person.Produce(p =>
            {
                p.LastName = "Bravo";
                p.FirstChild.FirstChild = p;
            });

            // A circular reference "unrolls" by forming a tree, pointing to the
            // older version, whenever the draft containing the circular reference
            // pointer is updated.  The older version will keep the circular reference.
            Assert.NotSame(initial, result);
            Assert.Equal("Bravo", result.LastName);
            Assert.Same(result, result.FirstChild.FirstChild);
            Assert.Equal("Bravo", result.FirstChild.FirstChild.LastName);
        }
Example #2
0
        public void CollectionNestedChangeTest()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                },
            };

            var person = ITestPerson.Produce(initial);

            var result = person.Produce(p =>
            {
                p.Cars[1].Make = "Shelby American";
            });

            Assert.NotSame(initial, result);
            Assert.NotSame(initial.Cars, result.Cars);
            Assert.Same(initial.Cars[0], result.Cars[0]);
            Assert.NotSame(initial.Cars[1], result.Cars[1]);
        }
Example #3
0
        public void CircularReferenceWithPointerUpdateTest()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
            };

            // this is really weird.
            initial.FirstChild = initial;

            ITestPerson person = ITestPerson.Produce(initial);

            var result = person.Produce(p =>
            {
                p.LastName   = "Bravo";
                p.FirstChild = p;
            });

            Assert.NotSame(initial, result);
            Assert.Equal("Bravo", result.LastName);
            Assert.Same(result, result.FirstChild);
            Assert.Equal("Bravo", result.FirstChild.LastName);
        }
Example #4
0
        public void UnchangedCollectionTest()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                },
            };

            var person = ITestPerson.Produce(initial);

            var result = person.Produce(p =>
            {
                foreach (var car in p.Cars)
                {
                    Assert.True(car.IsDraft());
                }
            });

            Assert.Same(person.Cars, result.Cars);
        }
Example #5
0
        public void NestedDraftRevokedTest()
        {
            ITestPerson initial = ITestPerson.Produce(new TestPerson()
            {
                FirstName  = "John",
                LastName   = "Doe",
                IsAdult    = true,
                FirstChild = new TestPerson()
                {
                    FirstName = "Baby",
                    LastName  = "Doe",
                    IsAdult   = false,
                },
            });

            TestPerson?draft      = null;
            TestPerson?draftChild = null;

            ITestPerson person = initial.Produce(p =>
            {
                draft      = p;
                draftChild = p.FirstChild;
            });

            Assert.Throws <DraftRevokedException>(() =>
            {
                Assert.Equal(draft !.FirstName, person.FirstName);
            });
            Assert.Throws <DraftRevokedException>(() =>
            {
                Assert.Equal(draftChild !.FirstName, person.FirstChild.FirstName);
            });
        }
Example #6
0
        public void NestedProduceRollbackTest()
        {
            ITestPerson initial = ITestPerson.Produce(new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                },
            });

            Car?car = null;

            var result = initial.Produce(p =>
            {
                p.Cars[0].Make  = "Ford";
                p.Cars[0].Model = "Fiesta";
                try
                {
                    p.Cars[0] = (Car)ICar.Produce(p.Cars[0], c =>
                    {
                        c.Make  = "Tesla";
                        c.Model = "Model 3";
                        car     = c;
                        throw new InvalidOperationException("Cars cannot be changed.");
                    });
                }
                catch (InvalidOperationException)
                {
                    // car is revoked as it was inside the nested produce.
                    Assert.Throws <DraftRevokedException>(() => car !.Make);
                }
            });

            Assert.NotSame(initial, result);

            // We rolled back the changes on the inner produce, but we did keep the drafts for the outer changes.
            Assert.Equal("Ford", result.Cars[0].Make);
            Assert.Equal("Fiesta", result.Cars[0].Model);
        }
Example #7
0
        public void NestedProduceCanContinueTest()
        {
            ITestPerson initial = ITestPerson.Produce(new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                },
            });

            var crasher = ICar.Producer(car =>
            {
                car.Crashed = true;
            });

            var fixer = ICar.Producer(car =>
            {
                car.Crashed = false;
            });

            var result = initial.Produce(p =>
            {
                p.LastName     = "SadDoe";
                var crashedCar = (Car)crasher(p.Cars[0]);
                p.Cars[0]      = crashedCar;
                p.Cars[0].Make = "Enzo Ferrari";
                var fixedCar   = (Car)fixer(p.Cars[0]);
                Assert.NotSame(crashedCar, fixedCar);
                p.Cars[0] = fixedCar;
            });

            Assert.NotSame(initial, result);
            Assert.Equal("SadDoe", result.LastName);
            Assert.False(result.Cars[0].Crashed);
            Assert.Equal("Enzo Ferrari", result.Cars[0].Make);
        }
Example #8
0
        public void NestedLockedTest()
        {
            var initial = new TestPerson()
            {
                FirstName  = "John",
                LastName   = "Doe",
                IsAdult    = true,
                FirstChild = new TestPerson()
                {
                    FirstName  = "Baby",
                    LastName   = "Doe",
                    IsAdult    = false,
                    FirstChild = new TestPerson()
                    {
                        FirstName  = "Mika",
                        LastName   = "Doe",
                        IsAdult    = false,
                        FirstChild = new TestPerson()
                        {
                            FirstName = "Play",
                            LastName  = "Doe",
                            IsAdult   = false,
                        },
                    },
                },
            };

            ITestPerson person = ITestPerson.Produce(initial);

            var mutablePerson = (TestPerson)person;

            Assert.Throws <ImmutableException>(() =>
            {
                mutablePerson.FirstName = "Test";
            });
            Assert.Throws <ImmutableException>(() =>
            {
                mutablePerson.FirstChild.FirstName = "Test";
            });
            Assert.Throws <ImmutableException>(() =>
            {
                mutablePerson.FirstChild.FirstChild.FirstName = "Test";
            });
            Assert.Throws <ImmutableException>(() =>
            {
                mutablePerson.FirstChild.FirstChild.FirstChild.FirstName = "Test";
            });
        }
Example #9
0
        public void InnerNestedChangeTest()
        {
            var initial = new TestPerson()
            {
                FirstName  = "John",
                LastName   = "Doe",
                IsAdult    = true,
                FirstChild = new TestPerson()
                {
                    FirstName  = "Baby",
                    LastName   = "Doe",
                    IsAdult    = false,
                    FirstChild = new TestPerson()
                    {
                        FirstName  = "Mika",
                        LastName   = "Doe",
                        IsAdult    = false,
                        FirstChild = new TestPerson()
                        {
                            FirstName = "Play",
                            LastName  = "Doe",
                            IsAdult   = false,
                        },
                    },
                },
            };

            ITestPerson person = ITestPerson.Produce(initial);

            var result = person.Produce(p =>
            {
                p.FirstChild.FirstChild.LastName = "Anon";
            });

            // Copy on write.
            Assert.Same(initial, person);
            Assert.Same(initial.FirstChild, person.FirstChild);
            Assert.Same(initial.FirstChild.FirstChild, person.FirstChild.FirstChild);
            Assert.Same(initial.FirstChild.FirstChild.FirstChild, person.FirstChild.FirstChild.FirstChild);

            Assert.NotSame(person, result);
            Assert.NotSame(person.FirstChild, result.FirstChild);
            Assert.NotSame(person.FirstChild.FirstChild, result.FirstChild.FirstChild);

            // this is the same immutable as it did not change.
            Assert.Same(person.FirstChild.FirstChild.FirstChild, result.FirstChild.FirstChild.FirstChild);
        }
        public void InitialProduceTest()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
            };

            ITestPerson person = ITestPerson.Produce(initial);

            // Copy on write so the objects should be identical.
            Assert.Same(initial, person);
            Assert.Equal(initial.FirstName, person.FirstName);
            Assert.Equal(initial.LastName, person.LastName);
            Assert.Equal(initial.IsAdult, person.IsAdult);
        }
        public void LockedTest()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
            };

            ITestPerson person = ITestPerson.Produce(initial);

            Assert.Throws <ImmutableException>(() =>
            {
                var mutablePerson = (TestPerson)person;

                mutablePerson.FirstName = "Test";
            });
        }
        public void StaticProduceActionTest()
        {
            TestPerson initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
            };

            ITestPerson person = ITestPerson.Produce(initial, p =>
            {
                p.FirstName = "Jane";
                p.IsAdult   = false;
            });

            Assert.NotSame(initial, person);
            Assert.NotEqual(initial.FirstName, person.FirstName);
            Assert.Equal(initial.LastName, person.LastName);
            Assert.NotEqual(initial.IsAdult, person.IsAdult);
        }
        public void GenerateAndApplyPatch()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
            };

            using var scope = DraftExtensions.CreateDraft(initial, out TestPerson draft);

            draft.FirstName  = "Jane";
            draft.LastName   = null;
            draft.FirstChild = new TestPerson()
            {
                FirstName = "Baby",
                LastName  = "Doe",
            };

            var patchGenerator = new ObjectPatchGenerator();
            var patches        = new JsonPatchDocument();
            var inversePatches = new JsonPatchDocument();

            patchGenerator.Generate((IDraft)draft, "/", patches, inversePatches);

            // inverse order of inverse patches.
            inversePatches.Operations.Reverse();

            var final = scope.FinishDraft <TestPerson, ITestPerson>(draft);

            var result = ITestPerson.Produce(initial, p =>
            {
                patches.ApplyTo(p);
            });

            Assert.Equal(final.FirstName, result.FirstName);
            Assert.Equal(final.LastName, result.LastName);
            Assert.Equal(final.FirstChild.FirstName, result.FirstChild.FirstName);
            Assert.Equal(final.FirstChild.LastName, result.FirstChild.LastName);
        }
        public void DontDraftNewState()
        {
            ITestPerson initial = ITestPerson.Produce(new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
            });

            initial.Produce(p =>
            {
                var child = new TestPerson()
                {
                    FirstName = "Baby",
                    LastName  = "Joe,",
                    IsAdult   = false,
                };
                p.FirstChild = child;
                Assert.Same(child, p.FirstChild);
                Assert.False(p.FirstChild.IsDraft());
            });
        }
Example #15
0
        public void CollectionProduceTest()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                },
            };

            var person = ITestPerson.Produce(initial);

            var result = person.Produce(p =>
            {
                p.Cars.Add(new Car()
                {
                    Make  = "Rolls Royce",
                    Model = "10 HP",
                });
            });

            Assert.NotSame(initial, result);
            Assert.NotSame(initial.Cars, result.Cars);
            Assert.Equal(3, result.Cars.Count);
            Assert.True(((ILockable)result.Cars.Last()).Locked);
        }
        public void ProduceFunctionTest()
        {
            ITestPerson initial = ITestPerson.Produce(
                new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
            });

            ITestPerson person = initial.Produce(() =>
                                                 new TestPerson()
            {
                FirstName = "Jane",
                LastName  = "Doe",
                IsAdult   = false,
            });

            Assert.NotSame(initial, person);
            Assert.NotEqual(initial.FirstName, person.FirstName);
            Assert.Equal(initial.LastName, person.LastName);
            Assert.NotEqual(initial.IsAdult, person.IsAdult);
        }
Example #17
0
        public void CollectionRevokedTest()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                },
            };

            var person = ITestPerson.Produce(initial);

            IList <Car>?cars = null;

            var result = person.Produce(p =>
            {
                cars = p.Cars;
            });

            Assert.NotSame(cars, result.Cars);
            Assert.Throws <DraftRevokedException>(() =>
            {
                Assert.Same(result.Cars[0], cars ![0]);
            });
Example #18
0
        public void ProduceWithPatchesTest()
        {
            ITestPerson initial = ITestPerson.Produce(new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                },
            });

            var patches        = new JsonPatchDocument();
            var inversePatches = new JsonPatchDocument();

            var options = ProducerOptions.Default.WithPatches(patches, inversePatches);

            var result = initial.Produce(
                p =>
            {
                p.LastName        = "SadDoe";
                p.Cars[0].Crashed = true;
            }, options);

            JsonAssert.Equal(
                @"
            [
              {
                'value': true,
                'path': '/Cars/0/Crashed',
                'op': 'replace'
              },
              {
                'value': 'SadDoe',
                'path': '/LastName',
                'op': 'replace'
              }
            ]
            ", JsonConvert.SerializeObject(patches));

            JsonAssert.Equal(
                @"
            [
              {
                'value': 'Doe',
                'path': '/LastName',
                'op': 'replace'
              },
              {
                'value': false,
                'path': '/Cars/0/Crashed',
                'op': 'replace'
              }
            ]
            ", JsonConvert.SerializeObject(inversePatches));
        }
        public void ApplyInverseComplexCollectionPatch()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                    new Car()
                    {
                        Make  = "Rolls Royce",
                        Model = "10 HP",
                    },
                    new Car()
                    {
                        Make  = "Mercedes-Benz",
                        Model = "38/250 SSK",
                    },
                },
            };

            var         patches        = new JsonPatchDocument();
            var         inversePatches = new JsonPatchDocument();
            ITestPerson testPerson;

            using (DraftScope scope = (DraftScope)DraftExtensions.CreateDraft(initial, out TestPerson draft))
            {
                var patchGenerator = new CollectionPatchGenerator(new DynamicLargestCommonSubsequence());

                draft.Cars.RemoveAt(3);
                draft.Cars.RemoveAt(0);
                draft.Cars.Add(new Car()
                {
                    Make  = "Bugatti",
                    Model = "Type 57 SC Atalante",
                });

                // trick the scope into thinking that is finishing and should not create proxies anymore.
                scope.IsFinishing = true;

                patchGenerator.Generate((IDraft)draft.Cars, "/Cars", patches, inversePatches);

                // inverse order of inverse patches.
                inversePatches.Operations.Reverse();

                testPerson = scope.FinishDraft <ITestPerson, TestPerson>(draft);
            }

            var result = ITestPerson.Produce(initial, p =>
            {
                patches.ApplyTo(p);
            });

            result = result.Produce(p =>
            {
                inversePatches.ApplyTo(p);
            });

            Assert.Equal(4, result.Cars.Count);
            Assert.Equal("Ferrari", result.Cars[0].Make);
            Assert.Equal("250 LM", result.Cars[0].Model);
            Assert.Equal("Shelby", result.Cars[1].Make);
            Assert.Equal("Daytona Cobra Coupe", result.Cars[1].Model);
            Assert.Equal("Rolls Royce", result.Cars[2].Make);
            Assert.Equal("10 HP", result.Cars[2].Model);
            Assert.Equal("Mercedes-Benz", result.Cars[3].Make);
            Assert.Equal("38/250 SSK", result.Cars[3].Model);
        }
        public void ApplyTrivialCollectionRemovalPatch()
        {
            var initial = new TestPerson()
            {
                FirstName = "John",
                LastName  = "Doe",
                IsAdult   = true,
                Cars      = new List <Car>()
                {
                    new Car
                    {
                        Make  = "Ferrari",
                        Model = "250 LM",
                    },
                    new Car
                    {
                        Make  = "Shelby",
                        Model = "Daytona Cobra Coupe",
                    },
                    new Car()
                    {
                        Make  = "Rolls Royce",
                        Model = "10 HP",
                    },
                    new Car()
                    {
                        Make  = "Mercedes-Benz",
                        Model = "38/250 SSK",
                    },
                    new Car()
                    {
                        Make  = "Bugatti",
                        Model = "Type 57 SC Atalante",
                    },
                },
            };
            var patches        = new JsonPatchDocument();
            var inversePatches = new JsonPatchDocument();

            ITestPerson testPerson;

            using (var scope = DraftExtensions.CreateDraft(initial, out TestPerson draft))
            {
                var patchGenerator = new CollectionPatchGenerator(new DynamicLargestCommonSubsequence());

                draft.Cars.RemoveAt(2);
                draft.Cars.RemoveAt(2);
                draft.Cars.RemoveAt(2);

                patchGenerator.Generate((IDraft)draft.Cars, "/Cars", patches, inversePatches);

                // inverse order of inverse patches.
                inversePatches.Operations.Reverse();
                testPerson = scope.FinishDraft <ITestPerson, TestPerson>(draft);
            }

            // inverse order of inverse patches.
            inversePatches.Operations.Reverse();

            var result = ITestPerson.Produce(initial, p =>
            {
                patches.ApplyTo(p);
            });

            Assert.Equal(2, result.Cars.Count);
            Assert.Equal("Ferrari", result.Cars[0].Make);
            Assert.Equal("250 LM", result.Cars[0].Model);
            Assert.Equal("Shelby", result.Cars[1].Make);
            Assert.Equal("Daytona Cobra Coupe", result.Cars[1].Model);
        }