Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanholden8/21b1dddc4da428c1cfdc70da6ecf58f7 to your computer and use it in GitHub Desktop.
Save ryanholden8/21b1dddc4da428c1cfdc70da6ecf58f7 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using DynamicData;
using DynamicData.Binding;
using DynamicDataGist.TransformWithInlineUpdate;
using FluentAssertions;
using Microsoft.Reactive.Testing;
using ReactiveUI.Fody.Helpers;
using Xunit;
namespace WMS.Tests.Models
{
public class DynamicDataPipelineTests
{
[Fact]
public void SoureCacheTransformWithInlineUpdate_Emits_Refreshed()
{
// Arrange
var sourceCache = new SourceCache<Model, int>(model => model.Id);
var changeSetPipeline = sourceCache
.Connect()
.TransformWithInlineUpdate(
// The transformFactory is invoked when a new model is added to the upstream SourceCache
transformFactory: model => new ViewModel(model),
// The updateAction is invoked when a model in the upstream SourceCache has been replaced with a new model which has the same unique ID
updateAction: (existingViewModel, updatedModel) => existingViewModel.Model = updatedModel);
var changesOutput = new TestScheduler().CreateObserver(changeSetPipeline);
// Act
sourceCache.AddOrUpdate(Model.First);
sourceCache.AddOrUpdate(Model.Second);
sourceCache.RemoveKey(2);
sourceCache.AddOrUpdate(Model.FirstButUpdated);
// Assert
changesOutput
.Values()
.SelectMany(changeSet => changeSet.Select(change => (change.Reason, change.Current)))
.Should()
.BeInOrderSameTypeEquivalentTo(
(ChangeReason.Add, ViewModel.FirstButUpdated), // The same reference was updated already at this point so the model is updated.
(ChangeReason.Add, ViewModel.Second),
(ChangeReason.Remove, ViewModel.Second),
(ChangeReason.Refresh, ViewModel.FirstButUpdated));
changesOutput.OnCompleted();
sourceCache.Dispose();
}
[Fact]
public void CollectionTransformWithInlineUpdate_FailsToEmit_RefreshedOrAddRemove()
{
// Arrange
var sourceCache = new SourceCache<Model, int>(model => model.Id);
var disposable = sourceCache
.Connect()
.TransformWithInlineUpdate(
// The transformFactory is invoked when a new model is added to the upstream SourceCache
transformFactory: model => new ViewModel(model),
// The updateAction is invoked when a model in the upstream SourceCache has been replaced with a new model which has the same unique ID
updateAction: (existingViewModel, updatedModel) => existingViewModel.Model = updatedModel)
.Bind(out System.Collections.ObjectModel.ReadOnlyObservableCollection<ViewModel> collection)
.Subscribe();
// This is what a reactiveUI recycler view converts the collection into
// https://github.com/reactiveui/ReactiveUI/blob/master/src/ReactiveUI.AndroidSupport/ReactiveRecyclerViewAdapter.cs#L146
var changesOutput = new TestScheduler().CreateObserver(collection.ToObservableChangeSet());
// Act
sourceCache.AddOrUpdate(Model.First);
sourceCache.AddOrUpdate(Model.Second);
sourceCache.RemoveKey(2);
sourceCache.AddOrUpdate(Model.FirstButUpdated);
// Assert
changesOutput
.Values()
.SelectMany(changeSet => changeSet.Select(change => (change.Reason, change.Range?.FirstOrDefault() ?? change.Item.Current)))
.Should()
.BeInOrderSameTypeEquivalentTo(
(ListChangeReason.AddRange, ViewModel.FirstButUpdated), // The same reference was updated already at this point so the model is updated.
(ListChangeReason.Add, ViewModel.Second),
(ListChangeReason.Remove, ViewModel.Second)
// What was logically expected:
// (ListChangeReason.Refresh, SimpleViewModel.FirstButUpdated) or (ListChangeReason.Replace, SimpleViewModel.FirstButUpdated)
// Reality:
/* No change set at all, it appears when binding to a collection refreshes are ignored */);
changesOutput.OnCompleted();
sourceCache.Dispose();
disposable.Dispose();
}
#region Private
private class Model : IEquatable<Model>
{
public readonly static Model First = new Model(id: 1, name: "first");
public readonly static Model Second = new Model(id: 2, name: "second");
public readonly static Model FirstButUpdated = new Model(id: 1, name: "first but updated");
public int Id { get; }
public string Name { get; }
public Model(int id, string name)
{
Id = id;
Name = name;
}
public override string ToString() => $"{nameof(Model)}({Id}): {Name}";
public bool Equals([AllowNull] Model other) => Id == other?.Id && Name == other.Name;
}
private class ViewModel : ReactiveUI.ReactiveObject, IEquatable<ViewModel>
{
public readonly static ViewModel First = new ViewModel(Model.First);
public readonly static ViewModel Second = new ViewModel(Model.Second);
public readonly static ViewModel FirstButUpdated = new ViewModel(Model.FirstButUpdated);
[Reactive]
public Model Model { get; set; }
public ViewModel(Model model)
{
Model = model;
}
public override string ToString() => $"{nameof(ViewModel)}({Model.Id}): {Model.Name}";
public bool Equals([AllowNull] ViewModel other) => Model == other?.Model;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment