restorablepool.spec.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { RestorablePool } from '@jupyterlab/statedb';
  4. import { signalToPromise } from '@jupyterlab/testutils';
  5. import { IObservableDisposable } from '@lumino/disposable';
  6. import { ISignal, Signal } from '@lumino/signaling';
  7. const namespace = 'restorable-pool-test';
  8. class ObservableDisposable implements IObservableDisposable {
  9. constructor(public id: string = '') {
  10. // no-op
  11. }
  12. get disposed(): ISignal<this, void> {
  13. return this._disposed;
  14. }
  15. get isDisposed(): boolean {
  16. return this._isDisposed;
  17. }
  18. dispose(): void {
  19. if (this.isDisposed) {
  20. return;
  21. }
  22. this._isDisposed = true;
  23. this._disposed.emit(undefined);
  24. Signal.clearData(this);
  25. }
  26. private _disposed = new Signal<this, void>(this);
  27. private _isDisposed = false;
  28. }
  29. describe('@jupyterlab/coreutils', () => {
  30. describe('RestorablePool', () => {
  31. let pool: RestorablePool<ObservableDisposable>;
  32. beforeEach(() => {
  33. pool = new RestorablePool({ namespace });
  34. });
  35. afterEach(() => {
  36. pool.dispose();
  37. });
  38. describe('#constructor()', () => {
  39. it('should create a RestorablePool', () => {
  40. expect(pool).toBeInstanceOf(RestorablePool);
  41. });
  42. });
  43. describe('#added', () => {
  44. it('should emit when an instance has been added', async () => {
  45. const instance = new ObservableDisposable();
  46. const promise = signalToPromise(pool.added);
  47. await pool.add(instance);
  48. const [sender, args] = await promise;
  49. expect(sender).toBe(pool);
  50. expect(args).toBe(instance);
  51. instance.dispose();
  52. });
  53. });
  54. describe('#current', () => {
  55. it('should default to null', () => {
  56. expect(pool.current).toBeNull();
  57. });
  58. it('should be settable by client code', async () => {
  59. const instance = new ObservableDisposable();
  60. void pool.add(instance);
  61. expect(pool.current).toBe(null);
  62. pool.current = instance;
  63. expect(pool.current).toBe(instance);
  64. instance.dispose();
  65. });
  66. it('should be a no-op if set to an untracked instance', async () => {
  67. const instance = new ObservableDisposable();
  68. expect(pool.current).toBe(null);
  69. pool.current = instance;
  70. expect(pool.current).toBe(null);
  71. instance.dispose();
  72. });
  73. });
  74. describe('#currentChanged', () => {
  75. it('should emit when the current object has been updated', async () => {
  76. const instance = new ObservableDisposable();
  77. const promise = signalToPromise(pool.currentChanged);
  78. void pool.add(instance);
  79. pool.current = instance;
  80. await promise;
  81. instance.dispose();
  82. });
  83. });
  84. describe('#isDisposed', () => {
  85. it('should test whether the pool is disposed', () => {
  86. expect(pool.isDisposed).toBe(false);
  87. pool.dispose();
  88. expect(pool.isDisposed).toBe(true);
  89. });
  90. });
  91. describe('#add()', () => {
  92. it('should add an instance to the pool', async () => {
  93. const instance = new ObservableDisposable();
  94. expect(pool.has(instance)).toBe(false);
  95. await pool.add(instance);
  96. expect(pool.has(instance)).toBe(true);
  97. });
  98. it('should reject an instance that already exists', async () => {
  99. const instance = new ObservableDisposable();
  100. let failed = false;
  101. expect(pool.has(instance)).toBe(false);
  102. await pool.add(instance);
  103. expect(pool.has(instance)).toBe(true);
  104. try {
  105. await pool.add(instance);
  106. } catch (error) {
  107. failed = true;
  108. }
  109. expect(failed).toBe(true);
  110. });
  111. it('should reject an instance that is disposed', async () => {
  112. const instance = new ObservableDisposable();
  113. let failed = false;
  114. expect(pool.has(instance)).toBe(false);
  115. instance.dispose();
  116. try {
  117. await pool.add(instance);
  118. } catch (error) {
  119. failed = true;
  120. }
  121. expect(failed).toBe(true);
  122. });
  123. it('should remove an added instance if it is disposed', async () => {
  124. const instance = new ObservableDisposable();
  125. await pool.add(instance);
  126. expect(pool.has(instance)).toBe(true);
  127. instance.dispose();
  128. expect(pool.has(instance)).toBe(false);
  129. });
  130. });
  131. describe('#dispose()', () => {
  132. it('should dispose of the resources used by the pool', () => {
  133. expect(pool.isDisposed).toBe(false);
  134. pool.dispose();
  135. expect(pool.isDisposed).toBe(true);
  136. });
  137. it('should be safe to call multiple times', () => {
  138. expect(pool.isDisposed).toBe(false);
  139. pool.dispose();
  140. pool.dispose();
  141. expect(pool.isDisposed).toBe(true);
  142. });
  143. });
  144. describe('#find()', () => {
  145. it('should find a tracked item that matches a filter function', () => {
  146. const instanceA = new ObservableDisposable('A');
  147. const instanceB = new ObservableDisposable('B');
  148. const instanceC = new ObservableDisposable('C');
  149. void pool.add(instanceA);
  150. void pool.add(instanceB);
  151. void pool.add(instanceC);
  152. expect(pool.find(obj => obj.id === 'B')).toBe(instanceB);
  153. instanceA.dispose();
  154. instanceB.dispose();
  155. instanceC.dispose();
  156. });
  157. it('should return a void if no item is found', () => {
  158. const instanceA = new ObservableDisposable('A');
  159. const instanceB = new ObservableDisposable('B');
  160. const instanceC = new ObservableDisposable('C');
  161. void pool.add(instanceA);
  162. void pool.add(instanceB);
  163. void pool.add(instanceC);
  164. expect(pool.find(widget => widget.id === 'D')).toBeFalsy();
  165. instanceA.dispose();
  166. instanceB.dispose();
  167. instanceC.dispose();
  168. });
  169. });
  170. describe('#filter()', () => {
  171. it('should filter according to a predicate function', () => {
  172. const instanceA = new ObservableDisposable('include-A');
  173. const instanceB = new ObservableDisposable('include-B');
  174. const instanceC = new ObservableDisposable('exclude-C');
  175. void pool.add(instanceA);
  176. void pool.add(instanceB);
  177. void pool.add(instanceC);
  178. const list = pool.filter(obj => obj.id.indexOf('include') !== -1);
  179. expect(list.length).toBe(2);
  180. expect(list[0]).toBe(instanceA);
  181. expect(list[1]).toBe(instanceB);
  182. instanceA.dispose();
  183. instanceB.dispose();
  184. instanceC.dispose();
  185. });
  186. it('should return an empty array if no item is found', () => {
  187. const instanceA = new ObservableDisposable('A');
  188. const instanceB = new ObservableDisposable('B');
  189. const instanceC = new ObservableDisposable('C');
  190. void pool.add(instanceA);
  191. void pool.add(instanceB);
  192. void pool.add(instanceC);
  193. expect(pool.filter(widget => widget.id === 'D').length).toBe(0);
  194. instanceA.dispose();
  195. instanceB.dispose();
  196. instanceC.dispose();
  197. });
  198. });
  199. describe('#forEach()', () => {
  200. it('should iterate through all the tracked items', () => {
  201. const instanceA = new ObservableDisposable('A');
  202. const instanceB = new ObservableDisposable('B');
  203. const instanceC = new ObservableDisposable('C');
  204. let visited = '';
  205. void pool.add(instanceA);
  206. void pool.add(instanceB);
  207. void pool.add(instanceC);
  208. pool.forEach(obj => {
  209. visited += obj.id;
  210. });
  211. expect(visited).toBe('ABC');
  212. });
  213. });
  214. describe('#has()', () => {
  215. it('should return `true` if an item exists in the pool', () => {
  216. const instance = new ObservableDisposable();
  217. expect(pool.has(instance)).toBe(false);
  218. void pool.add(instance);
  219. expect(pool.has(instance)).toBe(true);
  220. });
  221. });
  222. });
  223. });