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); }
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]); }
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); }
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); }
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); }); }
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); }
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); }
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"; }); }
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()); }); }
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); }
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]); });
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); }