com.fofx.observething 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +2 -0
  2. package/README.md.meta +7 -0
  3. package/Runtime/CollectionIdProvider.cs +72 -0
  4. package/Runtime/CollectionIdProvider.cs.meta +2 -0
  5. package/Runtime/IObservable.cs +66 -0
  6. package/Runtime/IObservable.cs.meta +2 -0
  7. package/Runtime/Implementations/Observable.cs +30 -0
  8. package/Runtime/Implementations/Observable.cs.meta +2 -0
  9. package/Runtime/Implementations/ObservableBase.cs +173 -0
  10. package/Runtime/Implementations/ObservableBase.cs.meta +2 -0
  11. package/Runtime/Implementations/ObservableCollection.cs +141 -0
  12. package/Runtime/Implementations/ObservableCollection.cs.meta +2 -0
  13. package/Runtime/Implementations/ObservableDictionary.cs +166 -0
  14. package/Runtime/Implementations/ObservableDictionary.cs.meta +2 -0
  15. package/Runtime/Implementations/ObservableList.cs +163 -0
  16. package/Runtime/Implementations/ObservableList.cs.meta +2 -0
  17. package/Runtime/Implementations/ObservableSet.cs +137 -0
  18. package/Runtime/Implementations/ObservableSet.cs.meta +2 -0
  19. package/Runtime/Implementations/ObservableValue.cs +59 -0
  20. package/Runtime/Implementations/ObservableValue.cs.meta +2 -0
  21. package/Runtime/Implementations.meta +8 -0
  22. package/Runtime/Observables/BatchObservable.cs +72 -0
  23. package/Runtime/Observables/BatchObservable.cs.meta +2 -0
  24. package/Runtime/Observables/CastObservable.cs +128 -0
  25. package/Runtime/Observables/CastObservable.cs.meta +2 -0
  26. package/Runtime/Observables/CombineObservable.cs +96 -0
  27. package/Runtime/Observables/CombineObservable.cs.meta +2 -0
  28. package/Runtime/Observables/CombineValueObservable.cs +319 -0
  29. package/Runtime/Observables/CombineValueObservable.cs.meta +2 -0
  30. package/Runtime/Observables/ConcatObservable.cs +75 -0
  31. package/Runtime/Observables/ConcatObservable.cs.meta +2 -0
  32. package/Runtime/Observables/ContainsObservable.cs +71 -0
  33. package/Runtime/Observables/ContainsObservable.cs.meta +2 -0
  34. package/Runtime/Observables/CountObservable.cs +29 -0
  35. package/Runtime/Observables/CountObservable.cs.meta +2 -0
  36. package/Runtime/Observables/DistinctObservable.cs +55 -0
  37. package/Runtime/Observables/DistinctObservable.cs.meta +2 -0
  38. package/Runtime/Observables/FirstObservable.cs +53 -0
  39. package/Runtime/Observables/FirstObservable.cs.meta +2 -0
  40. package/Runtime/Observables/IndexOfObservable.cs +83 -0
  41. package/Runtime/Observables/IndexOfObservable.cs.meta +2 -0
  42. package/Runtime/Observables/OperationStreamObservable.cs +194 -0
  43. package/Runtime/Observables/OperationStreamObservable.cs.meta +2 -0
  44. package/Runtime/Observables/OrderByObservable.cs +116 -0
  45. package/Runtime/Observables/OrderByObservable.cs.meta +2 -0
  46. package/Runtime/Observables/SelectCollectionObservable.cs +29 -0
  47. package/Runtime/Observables/SelectCollectionObservable.cs.meta +2 -0
  48. package/Runtime/Observables/SelectListObservable.cs +29 -0
  49. package/Runtime/Observables/SelectListObservable.cs.meta +2 -0
  50. package/Runtime/Observables/SelectManyObservable.cs +78 -0
  51. package/Runtime/Observables/SelectManyObservable.cs.meta +2 -0
  52. package/Runtime/Observables/SelectValueObservable.cs +28 -0
  53. package/Runtime/Observables/SelectValueObservable.cs.meta +2 -0
  54. package/Runtime/Observables/SkipWhileObservable.cs +32 -0
  55. package/Runtime/Observables/SkipWhileObservable.cs.meta +2 -0
  56. package/Runtime/Observables/ThenObservable.cs +209 -0
  57. package/Runtime/Observables/ThenObservable.cs.meta +2 -0
  58. package/Runtime/Observables/ToDictionaryObservable.cs +35 -0
  59. package/Runtime/Observables/ToDictionaryObservable.cs.meta +2 -0
  60. package/Runtime/Observables/TrackObservable.cs +74 -0
  61. package/Runtime/Observables/TrackObservable.cs.meta +2 -0
  62. package/Runtime/Observables/UnwrapCollectionObservable.cs +64 -0
  63. package/Runtime/Observables/UnwrapCollectionObservable.cs.meta +2 -0
  64. package/Runtime/Observables/UnwrapListObservable.cs +69 -0
  65. package/Runtime/Observables/UnwrapListObservable.cs.meta +2 -0
  66. package/Runtime/Observables/UnwrapValueObservable.cs +56 -0
  67. package/Runtime/Observables/UnwrapValueObservable.cs.meta +2 -0
  68. package/Runtime/Observables/WhereObservable.cs +85 -0
  69. package/Runtime/Observables/WhereObservable.cs.meta +2 -0
  70. package/Runtime/Observables/WithPreviousObservable.cs +36 -0
  71. package/Runtime/Observables/WithPreviousObservable.cs.meta +2 -0
  72. package/Runtime/Observables.cs +467 -0
  73. package/Runtime/Observables.cs.meta +2 -0
  74. package/Runtime/Observables.meta +8 -0
  75. package/Runtime/ObservationContext.cs +126 -0
  76. package/Runtime/ObservationContext.cs.meta +2 -0
  77. package/Runtime/Observers.cs +403 -0
  78. package/Runtime/Observers.cs.meta +2 -0
  79. package/Runtime/Operators/BatchOperator.cs +71 -0
  80. package/Runtime/Operators/BatchOperator.cs.meta +2 -0
  81. package/Runtime/Operators/CollectionOperator.cs +30 -0
  82. package/Runtime/Operators/CollectionOperator.cs.meta +2 -0
  83. package/Runtime/Operators/DictionaryOperator.cs +45 -0
  84. package/Runtime/Operators/DictionaryOperator.cs.meta +2 -0
  85. package/Runtime/Operators/IInitializationOperationsProvider.cs +10 -0
  86. package/Runtime/Operators/IInitializationOperationsProvider.cs.meta +2 -0
  87. package/Runtime/Operators/ListOperator.cs +44 -0
  88. package/Runtime/Operators/ListOperator.cs.meta +2 -0
  89. package/Runtime/Operators/ObservableOperator.cs +75 -0
  90. package/Runtime/Operators/ObservableOperator.cs.meta +2 -0
  91. package/Runtime/Operators/Operator.cs +65 -0
  92. package/Runtime/Operators/Operator.cs.meta +2 -0
  93. package/Runtime/Operators/SetOperator.cs +44 -0
  94. package/Runtime/Operators/SetOperator.cs.meta +2 -0
  95. package/Runtime/Operators/ValueOperator.cs +30 -0
  96. package/Runtime/Operators/ValueOperator.cs.meta +2 -0
  97. package/Runtime/Operators.meta +8 -0
  98. package/Runtime/PriorityQueue.cs +1096 -0
  99. package/Runtime/PriorityQueue.cs.meta +2 -0
  100. package/Runtime/com.fofx.observething.asmdef +14 -0
  101. package/Runtime/com.fofx.observething.asmdef.meta +7 -0
  102. package/Runtime.meta +8 -0
  103. package/Tests/CollectionObservableTests.cs +739 -0
  104. package/Tests/CollectionObservableTests.cs.meta +2 -0
  105. package/Tests/DictionaryObservableTests.cs +85 -0
  106. package/Tests/DictionaryObservableTests.cs.meta +2 -0
  107. package/Tests/ListObservableTests.cs +148 -0
  108. package/Tests/ListObservableTests.cs.meta +2 -0
  109. package/Tests/ObservationContextTests.cs +359 -0
  110. package/Tests/ObservationContextTests.cs.meta +2 -0
  111. package/Tests/ValueObservableTests.cs +322 -0
  112. package/Tests/ValueObservableTests.cs.meta +2 -0
  113. package/Tests/com.fofx.observething.tests.asmdef +24 -0
  114. package/Tests/com.fofx.observething.tests.asmdef.meta +7 -0
  115. package/Tests.meta +8 -0
  116. package/package.json +24 -0
  117. package/package.json.meta +7 -0
@@ -0,0 +1,739 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using NUnit.Framework;
5
+ using UnityEngine.TestTools;
6
+
7
+ namespace ObserveThing.Tests
8
+ {
9
+ public class CollectionObservableTests
10
+ {
11
+ [SetUp]
12
+ public void SetUp()
13
+ {
14
+ Settings.DefaultExceptionHandler = UnityEngine.Debug.LogException;
15
+ }
16
+
17
+ private T Peek<T>(IValueObservable<T> observable)
18
+ {
19
+ T result = default;
20
+ var observer = observable.Subscribe(x => result = x);
21
+ observer.Dispose();
22
+ return result;
23
+ }
24
+
25
+ private List<T> Peek<T>(IListObservable<T> observable)
26
+ {
27
+ List<T> result = new List<T>();
28
+ var observer = observable.Subscribe((index, x) => result.Add(x));
29
+ observer.Dispose();
30
+ return result;
31
+ }
32
+
33
+ private void AreEqual<T>(T expected, IValueObservable<T> observable)
34
+ => Assert.AreEqual(expected, Peek(observable));
35
+
36
+ private void AreEqual<T>(IEnumerable<T> expected, IEnumerable<IValueObservable<T>> actual)
37
+ => Assert.AreEqual(expected, actual.Select(x => Peek(x)));
38
+
39
+ private void AreEqual<T>(IEnumerable<T> expected, IValueObservable<T> observable)
40
+ => Assert.AreEqual(expected, observable);
41
+
42
+ [Test]
43
+ public void TestOrderBy()
44
+ {
45
+ int callCount = 0;
46
+ List<ObservableValue<int>> results = new List<ObservableValue<int>>();
47
+ Exception exception = default;
48
+ bool disposed = false;
49
+
50
+ var list = new ObservableList<ObservableValue<int>>();
51
+ var orderBy = list.ObservableOrderBy(x => x.AsObservable()).Subscribe(
52
+ onAdd: (index, value) =>
53
+ {
54
+ callCount++;
55
+ results.Insert(index, value);
56
+ },
57
+ onRemove: (index, value) =>
58
+ {
59
+ callCount++;
60
+ results.RemoveAt(index);
61
+ },
62
+ onError: exc => exception = exc,
63
+ onDispose: () => disposed = true
64
+ );
65
+
66
+ var v1 = new ObservableValue<int>(2);
67
+
68
+ list.Add(new ObservableValue<int>(1));
69
+ list.Add(v1);
70
+ list.Add(new ObservableValue<int>(13));
71
+ list.Add(new ObservableValue<int>(2));
72
+ list.Add(new ObservableValue<int>(4));
73
+
74
+ Assert.AreEqual(5, callCount);
75
+
76
+ AreEqual(new int[] { 1, 2, 2, 4, 13 }, results);
77
+
78
+ list.Remove(results[1]);
79
+
80
+ Assert.AreEqual(6, callCount);
81
+ AreEqual(new int[] { 1, 2, 4, 13 }, results);
82
+
83
+ results[1].value = 22;
84
+
85
+ Assert.AreEqual(8, callCount);
86
+ AreEqual(new int[] { 1, 4, 13, 22 }, results);
87
+
88
+ results[2].value = -33;
89
+
90
+ Assert.AreEqual(10, callCount);
91
+ AreEqual(new int[] { -33, 1, 4, 22 }, results);
92
+
93
+ var multiAdd = new ObservableValue<int>(3);
94
+
95
+ list.Add(multiAdd);
96
+ list.Add(multiAdd);
97
+ list.Add(multiAdd);
98
+ list.Add(multiAdd);
99
+ list.Add(multiAdd);
100
+
101
+ Assert.AreEqual(15, callCount);
102
+ AreEqual(new int[] { -33, 1, 3, 3, 3, 3, 3, 4, 22 }, results);
103
+
104
+ multiAdd.value = 10;
105
+
106
+ Assert.AreEqual(25, callCount);
107
+ AreEqual(new int[] { -33, 1, 4, 10, 10, 10, 10, 10, 22 }, results);
108
+
109
+ list.Remove(multiAdd);
110
+ list.Remove(multiAdd);
111
+
112
+ Assert.AreEqual(27, callCount);
113
+ AreEqual(new int[] { -33, 1, 4, 10, 10, 10, 22 }, results);
114
+
115
+ multiAdd.value = 1;
116
+
117
+ Assert.AreEqual(33, callCount);
118
+ AreEqual(new int[] { -33, 1, 1, 1, 1, 4, 22 }, results);
119
+
120
+ multiAdd.value = 5;
121
+
122
+ Assert.AreEqual(39, callCount);
123
+ AreEqual(new int[] { -33, 1, 4, 5, 5, 5, 22 }, results);
124
+
125
+ orderBy.Dispose();
126
+ Assert.IsTrue(disposed);
127
+
128
+ list.Remove(results[1]);
129
+ Assert.AreEqual(39, callCount);
130
+ AreEqual(new int[] { -33, 1, 4, 5, 5, 5, 22 }, results);
131
+ }
132
+
133
+ [Test]
134
+ public void TestOrderByExternalCollection()
135
+ {
136
+ ObservableList<string> source = new ObservableList<string>();
137
+ List<string> destination = new List<string>();
138
+
139
+ source
140
+ .ObservableSelect(x => x)
141
+ .ObservableOrderBy(x => source.ObservableIndexOf(x).ObservableSelect(x => x.found ? x.index : -1))
142
+ .Subscribe(
143
+ onAdd: (index, value) => destination.Insert(index, value),
144
+ onRemove: (index, value) => destination.RemoveAt(index)
145
+ );
146
+
147
+ source.Add("cat");
148
+ source.Add("dog");
149
+ source.Add("frog");
150
+
151
+ Assert.That(destination, Is.EqualTo(source));
152
+
153
+ source.Remove("dog");
154
+ source.Add("me");
155
+ source.Insert(0, "meee");
156
+
157
+ Assert.That(destination, Is.EqualTo(source));
158
+ }
159
+
160
+ public struct TestStruct
161
+ {
162
+ public int value;
163
+ }
164
+
165
+ [Test]
166
+ public void TestOrderByExternalStructCollection()
167
+ {
168
+ ObservableList<TestStruct> source = new ObservableList<TestStruct>();
169
+ List<TestStruct> destination = new List<TestStruct>();
170
+
171
+ source
172
+ .ObservableSelect(x => x)
173
+ .ObservableWhere(x => true)
174
+ .ObservableOrderBy(x => source.ObservableIndexOf(x).ObservableSelect(x => x.found ? x.index : -1))
175
+ .Subscribe(
176
+ onAdd: (index, value) => destination.Insert(index, value),
177
+ onRemove: (index, value) => destination.RemoveAt(index)
178
+ );
179
+
180
+ source.Add(new TestStruct() { value = 1 });
181
+ source.Add(new TestStruct() { value = 2 });
182
+ source.Add(new TestStruct() { value = 3 });
183
+
184
+ Assert.That(destination, Is.EqualTo(source));
185
+
186
+ source.Remove(new TestStruct() { value = 2 });
187
+ source.Add(new TestStruct() { value = 4 });
188
+ source.Insert(0, new TestStruct() { value = 10 });
189
+
190
+ Assert.That(destination, Is.EqualTo(source));
191
+ }
192
+
193
+ [Test]
194
+ public void TestWhere()
195
+ {
196
+ int callCount = 0;
197
+ List<ObservableValue<int>> results = new List<ObservableValue<int>>();
198
+ Exception exception = default;
199
+ bool disposed = false;
200
+
201
+ var list = new ObservableList<ObservableValue<int>>();
202
+ var where = list.ObservableWhere(x => x.ObservableSelect(x => x % 2 == 0)).Subscribe(
203
+ onAdd: x =>
204
+ {
205
+ callCount++;
206
+ results.Add(x);
207
+ },
208
+ onRemove: x =>
209
+ {
210
+ callCount++;
211
+ results.Remove(x);
212
+ },
213
+ exc => exception = exc,
214
+ () => disposed = true
215
+ );
216
+
217
+ var v1 = new ObservableValue<int>(1);
218
+ var v2 = new ObservableValue<int>(6);
219
+ var v3 = new ObservableValue<int>(13);
220
+ var v4 = new ObservableValue<int>(2);
221
+ var v5 = new ObservableValue<int>(4);
222
+
223
+ list.Add(v1);
224
+ list.Add(v2);
225
+ list.Add(v3);
226
+ list.Add(v4);
227
+ list.Add(v5);
228
+
229
+ Assert.AreEqual(3, callCount);
230
+ Assert.That(results, Is.EquivalentTo(new[] { v2, v4, v5 }));
231
+
232
+ list.Remove(v4);
233
+
234
+ Assert.AreEqual(4, callCount);
235
+ Assert.That(results, Is.EquivalentTo(new[] { v2, v5 }));
236
+
237
+ list.Remove(v3);
238
+
239
+ Assert.AreEqual(4, callCount);
240
+ Assert.That(results, Is.EquivalentTo(new[] { v2, v5 }));
241
+
242
+ list.Add(v3);
243
+
244
+ Assert.AreEqual(4, callCount);
245
+ Assert.That(results, Is.EquivalentTo(new[] { v2, v5 }));
246
+
247
+ var v6 = new ObservableValue<int>(8);
248
+
249
+ list.Add(v6);
250
+
251
+ Assert.AreEqual(5, callCount);
252
+ Assert.That(results, Is.EquivalentTo(new[] { v2, v5, v6 }));
253
+
254
+ v6.value = 7;
255
+
256
+ Assert.AreEqual(6, callCount);
257
+ Assert.That(results, Is.EquivalentTo(new[] { v2, v5 }));
258
+
259
+ v2.value = 8;
260
+
261
+ Assert.AreEqual(6, callCount);
262
+ Assert.That(results, Is.EquivalentTo(new[] { v2, v5 }));
263
+
264
+ where.Dispose();
265
+ Assert.IsTrue(disposed);
266
+
267
+ list.Remove(v2);
268
+
269
+ Assert.AreEqual(6, callCount);
270
+ Assert.That(results, Is.EquivalentTo(new[] { v2, v5 }));
271
+ }
272
+
273
+ [Test]
274
+ public void TestDistinct()
275
+ {
276
+ int callCount = 0;
277
+ List<int> results = new List<int>();
278
+ Exception exception = default;
279
+ bool disposed = false;
280
+
281
+ var list = new ObservableList<int>();
282
+ var distinct = list.ObservableDistinct().Subscribe(
283
+ onAdd: x =>
284
+ {
285
+ callCount++;
286
+ results.Add(x);
287
+ },
288
+ onRemove: x =>
289
+ {
290
+ callCount++;
291
+ results.Remove(x);
292
+ },
293
+ exc => exception = exc,
294
+ () => disposed = true
295
+ );
296
+
297
+ list.Add(1);
298
+ list.Add(6);
299
+ list.Add(6);
300
+ list.Add(6);
301
+ list.Add(13);
302
+ list.Add(2);
303
+ list.Add(4);
304
+ list.Add(4);
305
+
306
+ Assert.AreEqual(5, callCount);
307
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 6, 2, 4, 13 }));
308
+
309
+ list.Remove(4);
310
+
311
+ Assert.AreEqual(5, callCount);
312
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 6, 2, 4, 13 }));
313
+
314
+ list.Remove(1);
315
+
316
+ Assert.AreEqual(6, callCount);
317
+ Assert.That(results, Is.EquivalentTo(new int[] { 6, 2, 4, 13 }));
318
+
319
+ list.Add(13);
320
+
321
+ Assert.AreEqual(6, callCount);
322
+ Assert.That(results, Is.EquivalentTo(new int[] { 6, 2, 4, 13 }));
323
+
324
+ distinct.Dispose();
325
+ Assert.IsTrue(disposed);
326
+
327
+ list.Add(100);
328
+
329
+ Assert.AreEqual(6, callCount);
330
+ Assert.That(results, Is.EquivalentTo(new int[] { 6, 2, 4, 13 }));
331
+ }
332
+
333
+ [Test]
334
+ public void TestConcat()
335
+ {
336
+ int callCount = 0;
337
+ List<int> results = new List<int>();
338
+ Exception exception = default;
339
+ bool disposed = false;
340
+
341
+ var list1 = new ObservableList<int>();
342
+ var list2 = new ObservableList<int>();
343
+ var concat = list1.ObservableConcat(list2.AsObservable()).Subscribe(
344
+ onAdd: x =>
345
+ {
346
+ callCount++;
347
+ results.Add(x);
348
+ },
349
+ onRemove: x =>
350
+ {
351
+ callCount++;
352
+ results.Remove(x);
353
+ },
354
+ exc => exception = exc,
355
+ () => disposed = true
356
+ );
357
+
358
+ list1.Add(1);
359
+ list1.Add(2);
360
+ list1.Add(3);
361
+
362
+ list2.Add(4);
363
+ list2.Add(5);
364
+ list2.Add(6);
365
+
366
+ Assert.AreEqual(6, callCount);
367
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 5, 6 }));
368
+
369
+ list2.Add(7);
370
+
371
+ Assert.AreEqual(7, callCount);
372
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 5, 6, 7 }));
373
+
374
+ list2.Remove(4);
375
+
376
+ Assert.AreEqual(8, callCount);
377
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 5, 6, 7 }));
378
+
379
+ list1.Add(8);
380
+
381
+ Assert.AreEqual(9, callCount);
382
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 5, 6, 7, 8 }));
383
+
384
+ list1.Remove(3);
385
+
386
+ Assert.AreEqual(10, callCount);
387
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 5, 6, 7, 8 }));
388
+
389
+ list2.Add(8);
390
+
391
+ Assert.AreEqual(11, callCount);
392
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 5, 6, 7, 8, 8 }));
393
+
394
+ list1.Remove(8);
395
+
396
+ Assert.AreEqual(12, callCount);
397
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 5, 6, 7, 8 }));
398
+
399
+ list1.Dispose();
400
+ Assert.IsTrue(disposed);
401
+
402
+ list2.Add(100);
403
+
404
+ Assert.AreEqual(12, callCount);
405
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 5, 6, 7, 8 }));
406
+ }
407
+
408
+ [Test]
409
+ public void TestSelectMany()
410
+ {
411
+ int callCount = 0;
412
+ List<int> results = new List<int>();
413
+ Exception exception = default;
414
+ bool disposed = false;
415
+
416
+ var list = new ObservableList<ObservableList<int>>();
417
+ var selectMany = list.ObservableSelectMany(x => x.AsObservable()).Subscribe(
418
+ x =>
419
+ {
420
+ callCount++;
421
+ results.Add(x);
422
+ },
423
+ x =>
424
+ {
425
+ callCount++;
426
+ results.Remove(x);
427
+ },
428
+ exc => exception = exc,
429
+ () => disposed = true
430
+ );
431
+
432
+ var arr1 = new ObservableList<int>(new[] { 1, 2, 3 });
433
+ var arr2 = new ObservableList<int>(new[] { 4, 5, 6 });
434
+ var arr3 = new ObservableList<int>(new[] { 7, 8, 9 });
435
+
436
+ list.Add(arr1);
437
+ list.Add(arr2);
438
+ list.Add(arr3);
439
+
440
+ Assert.AreEqual(9, callCount);
441
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }));
442
+
443
+ list.Remove(arr2);
444
+
445
+ Assert.AreEqual(12, callCount);
446
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 7, 8, 9 }));
447
+
448
+ list.Add(arr2);
449
+ list.Add(arr2);
450
+
451
+ Assert.AreEqual(18, callCount);
452
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9 }));
453
+
454
+ arr1.Add(100);
455
+
456
+ Assert.AreEqual(19, callCount);
457
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9, 100 }));
458
+
459
+ arr3.Remove(9);
460
+
461
+ Assert.AreEqual(20, callCount);
462
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 100 }));
463
+
464
+ arr2.Remove(5);
465
+
466
+ Assert.AreEqual(22, callCount);
467
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 6, 4, 6, 7, 8, 100 }));
468
+
469
+ arr2.Add(44);
470
+
471
+ Assert.AreEqual(24, callCount);
472
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 6, 4, 6, 7, 8, 100, 44, 44 }));
473
+
474
+ list.Dispose();
475
+ Assert.IsTrue(disposed);
476
+
477
+ Assert.Throws(typeof(ObjectDisposedException), () => list.Add(arr1));
478
+
479
+ Assert.AreEqual(24, callCount);
480
+ Assert.That(results, Is.EquivalentTo(new int[] { 1, 2, 3, 4, 6, 4, 6, 7, 8, 100, 44, 44 }));
481
+ }
482
+
483
+ [Test]
484
+ public void TestSelect()
485
+ {
486
+ var result = new List<string>();
487
+ var list = new ObservableList<int>();
488
+ bool disposed = false;
489
+ bool receivedCall = false;
490
+
491
+ var select = list.ObservableSelect(x => x.ToString()).Subscribe(
492
+ onAdd: x =>
493
+ {
494
+ result.Add(x);
495
+ receivedCall = true;
496
+ },
497
+ onRemove: x =>
498
+ {
499
+ result.Remove(x);
500
+ receivedCall = true;
501
+ },
502
+ onDispose: () => disposed = true
503
+ );
504
+
505
+ list.Add(1);
506
+ list.Add(1);
507
+ list.Add(2);
508
+ list.Add(3);
509
+ list.Add(4);
510
+ list.Add(45);
511
+
512
+ CollectionAssert.AreEquivalent(
513
+ Enumerable.Select(list, x => x.ToString()),
514
+ result
515
+ );
516
+
517
+ list.Remove(3);
518
+ list.Remove(3);
519
+ list.Add(1);
520
+ list.Add(45);
521
+ list.Remove(45);
522
+
523
+ CollectionAssert.AreEquivalent(
524
+ Enumerable.Select(list, x => x.ToString()),
525
+ result
526
+ );
527
+
528
+ list.Clear();
529
+
530
+ CollectionAssert.AreEquivalent(
531
+ Enumerable.Select(list, x => x.ToString()),
532
+ result
533
+ );
534
+
535
+ receivedCall = false;
536
+ select.Dispose();
537
+
538
+ list.Add(234);
539
+
540
+ Assert.IsTrue(disposed);
541
+ Assert.IsFalse(receivedCall);
542
+ }
543
+
544
+ [Test]
545
+ public void TestShallowCopy()
546
+ {
547
+ bool disposed = false;
548
+ bool callReceived = false;
549
+ var result = new List<float>();
550
+ var source = new ObservableList<ObservableValue<float>>(
551
+ new ObservableValue<float>(1),
552
+ new ObservableValue<float>(2),
553
+ new ObservableValue<float>(3)
554
+ );
555
+
556
+ var stream = source.ObservableUnwrap().Subscribe(
557
+ onAdd: (_, x) =>
558
+ {
559
+ result.Add(x);
560
+ callReceived = true;
561
+ },
562
+ onRemove: (_, x) =>
563
+ {
564
+ result.Remove(x);
565
+ callReceived = true;
566
+ },
567
+ onDispose: () => disposed = true
568
+ );
569
+
570
+ Assert.That(result, Is.EquivalentTo(new float[] { 1, 2, 3 }));
571
+
572
+ ((ObservableValue<float>)source[1]).value = 3;
573
+
574
+ Assert.That(result, Is.EquivalentTo(new float[] { 1, 3, 3 }));
575
+
576
+ var removed = source[1];
577
+ source.RemoveAt(1);
578
+
579
+ Assert.That(result, Is.EquivalentTo(new float[] { 1, 3 }));
580
+
581
+ ((ObservableValue<float>)removed).value = 100;
582
+
583
+ Assert.That(result, Is.EquivalentTo(new float[] { 1, 3 }));
584
+
585
+ source.Add(new ObservableValue<float>(55));
586
+ source.Add(new ObservableValue<float>(55));
587
+ source.Add(new ObservableValue<float>(1));
588
+
589
+ Assert.That(result, Is.EquivalentTo(new float[] { 1, 1, 3, 55, 55 }));
590
+
591
+ source.Clear();
592
+
593
+ Assert.That(result, Is.EquivalentTo(new float[] { }));
594
+
595
+ callReceived = false;
596
+ stream.Dispose();
597
+
598
+ source.Add(new ObservableValue<float>(100));
599
+ Assert.IsTrue(disposed);
600
+ Assert.IsFalse(callReceived);
601
+ }
602
+
603
+ public class TestElement
604
+ {
605
+ public ObservableValue<int> intValue = new ObservableValue<int>();
606
+ public ObservableValue<string> stringValue = new ObservableValue<string>();
607
+ }
608
+
609
+ [Test]
610
+ public void TestErrorLog()
611
+ {
612
+ var dict = new ObservableDictionary<int, string>();
613
+ var stream = dict.ObservableSelect(x =>
614
+ {
615
+ if (x.Key != 0)
616
+ throw new Exception("Oh no! First");
617
+
618
+ return x.Key;
619
+
620
+ }).ObservableSelect(x =>
621
+ {
622
+ if (x != 0)
623
+ throw new Exception("Oh no! Second");
624
+
625
+ return x;
626
+
627
+ }).Subscribe(onAdd: null, onRemove: null, onError: null, onDispose: null);
628
+
629
+ LogAssert.Expect(UnityEngine.LogType.Exception, "Exception: Oh no! First");
630
+ dict.Add(10, "cat");
631
+
632
+ LogAssert.Expect(UnityEngine.LogType.Exception, "Exception: Oh no! First");
633
+ dict.Add(11, "dog");
634
+ }
635
+
636
+ [Test]
637
+ public void TestFirst()
638
+ {
639
+ (bool found, ObservableValue<int> value) result = default;
640
+ bool receivedCall = false;
641
+ bool disposed = false;
642
+
643
+ var source = new ObservableList<ObservableValue<int>>(
644
+ new ObservableValue<int>(1),
645
+ new ObservableValue<int>(4),
646
+ new ObservableValue<int>(4),
647
+ new ObservableValue<int>(13)
648
+ );
649
+
650
+ var subscription = source.ObservableFirst(x => x.ObservableSelect(x => x == 4))
651
+ .Subscribe(
652
+ onNext: x =>
653
+ {
654
+ result = new(x.found, x.value);
655
+ receivedCall = true;
656
+ },
657
+ onDispose: () => disposed = true
658
+ );
659
+
660
+ Assert.IsTrue(result.found);
661
+ Assert.AreEqual(source[1], result.value);
662
+
663
+ source[1].value = 100;
664
+
665
+ Assert.IsTrue(result.found);
666
+ Assert.AreEqual(source[2], result.value);
667
+
668
+ source.RemoveAt(2);
669
+
670
+ Assert.IsFalse(result.found);
671
+ Assert.IsNull(result.value);
672
+
673
+ source[0].value = 4;
674
+
675
+ Assert.IsTrue(result.found);
676
+ Assert.AreEqual(source[0], result.value);
677
+
678
+ source.Dispose();
679
+
680
+ Assert.IsTrue(disposed);
681
+
682
+ receivedCall = false;
683
+ source[0].value = 0;
684
+
685
+ Assert.IsFalse(receivedCall);
686
+ }
687
+
688
+ [Test]
689
+ public void TestFirstOrDefault()
690
+ {
691
+ ObservableValue<int> result = default;
692
+ bool receivedCall = false;
693
+ bool disposed = false;
694
+
695
+ var source = new ObservableList<ObservableValue<int>>(
696
+ new ObservableValue<int>(1),
697
+ new ObservableValue<int>(4),
698
+ new ObservableValue<int>(4),
699
+ new ObservableValue<int>(13)
700
+ );
701
+
702
+ var subscription = source.ObservableFirstOrDefault(x => x.ObservableSelect(x => x == 4))
703
+ .Subscribe(
704
+ onNext: x =>
705
+ {
706
+ result = x;
707
+ receivedCall = true;
708
+ },
709
+ onDispose: () => disposed = true
710
+ );
711
+
712
+ Assert.IsNotNull(result);
713
+ Assert.AreEqual(source[1], result);
714
+
715
+ source[1].value = 100;
716
+
717
+ Assert.IsNotNull(result);
718
+ Assert.AreEqual(source[2], result);
719
+
720
+ source.RemoveAt(2);
721
+
722
+ Assert.IsNull(result);
723
+
724
+ source[0].value = 4;
725
+
726
+ Assert.IsNotNull(result);
727
+ Assert.AreEqual(source[0], result);
728
+
729
+ source.Dispose();
730
+
731
+ Assert.IsTrue(disposed);
732
+
733
+ receivedCall = false;
734
+ source[0].value = 0;
735
+
736
+ Assert.IsFalse(receivedCall);
737
+ }
738
+ }
739
+ }