| @@ -0,0 +1,33 | |||||
|
|
1 | import { ICancellation } from "./interfaces"; | |||
|
|
2 | ||||
|
|
3 | export class Cancellation implements ICancellation { | |||
|
|
4 | isSupported(): boolean { | |||
|
|
5 | return false; | |||
|
|
6 | } | |||
|
|
7 | throwIfRequested(): void { | |||
|
|
8 | } | |||
|
|
9 | ||||
|
|
10 | isRequested(): boolean { | |||
|
|
11 | return false; | |||
|
|
12 | } | |||
|
|
13 | ||||
|
|
14 | register(_cb: (e:any) => void): void { | |||
|
|
15 | ||||
|
|
16 | } | |||
|
|
17 | ||||
|
|
18 | static readonly none : Cancellation = { | |||
|
|
19 | isSupported(): boolean { | |||
|
|
20 | return false; | |||
|
|
21 | }, | |||
|
|
22 | ||||
|
|
23 | throwIfRequested(): void { | |||
|
|
24 | }, | |||
|
|
25 | ||||
|
|
26 | isRequested(): boolean { | |||
|
|
27 | return false; | |||
|
|
28 | }, | |||
|
|
29 | ||||
|
|
30 | register(_cb: (e:any) => void): void { | |||
|
|
31 | } | |||
|
|
32 | }; | |||
|
|
33 | } No newline at end of file | |||
| @@ -0,0 +1,85 | |||||
|
|
1 | import { IObservable, IObserver, IDestroyable, ICancellation } from '../interfaces'; | |||
|
|
2 | import { Cancellation } from '../Cancellation' | |||
|
|
3 | import * as TraceSource from '../log/TraceSource' | |||
|
|
4 | import { argumentNotNull } from '../safe'; | |||
|
|
5 | ||||
|
|
6 | const trace = TraceSource.get('@implab/core/components/Observable'); | |||
|
|
7 | ||||
|
|
8 | ||||
|
|
9 | class Observable<T> implements IObservable<T> { | |||
|
|
10 | private _once = new Array<IObserver<T>>(); | |||
|
|
11 | ||||
|
|
12 | private readonly _observers = new Array<IObserver<T>>(); | |||
|
|
13 | ||||
|
|
14 | constructor(func: (notify: IObserver<T>) => void) { | |||
|
|
15 | argumentNotNull(func, "func"); | |||
|
|
16 | ||||
|
|
17 | func(this._notify.bind(this)); | |||
|
|
18 | } | |||
|
|
19 | ||||
|
|
20 | on(observer: IObserver<T>): IDestroyable { | |||
|
|
21 | argumentNotNull(observer, "observer"); | |||
|
|
22 | ||||
|
|
23 | this._observers.push(observer); | |||
|
|
24 | ||||
|
|
25 | let me = this; | |||
|
|
26 | return { | |||
|
|
27 | destroy() { | |||
|
|
28 | me._removeObserver(observer); | |||
|
|
29 | } | |||
|
|
30 | } | |||
|
|
31 | } | |||
|
|
32 | ||||
|
|
33 | wait(ct: ICancellation = Cancellation.none): Promise<T> { | |||
|
|
34 | return new Promise<T>((resolve, reject) => { | |||
|
|
35 | this._once.push(resolve); | |||
|
|
36 | if (ct.isSupported()) { | |||
|
|
37 | ct.register((e) => { | |||
|
|
38 | this._removeOnce(resolve); | |||
|
|
39 | reject(e); | |||
|
|
40 | }); | |||
|
|
41 | } | |||
|
|
42 | }); | |||
|
|
43 | } | |||
|
|
44 | ||||
|
|
45 | onObserverException(e: any) { | |||
|
|
46 | trace.error("Unhandled exception in the observer: {0}", e); | |||
|
|
47 | } | |||
|
|
48 | ||||
|
|
49 | private _removeOnce(d: IObserver<T>) { | |||
|
|
50 | let i = this._once.indexOf(d); | |||
|
|
51 | if (i >= 0) | |||
|
|
52 | this._once.splice(i); | |||
|
|
53 | } | |||
|
|
54 | ||||
|
|
55 | private _removeObserver(d: IObserver<T>) { | |||
|
|
56 | let i = this._observers.indexOf(d); | |||
|
|
57 | if (i >= 0) | |||
|
|
58 | this._observers.splice(i); | |||
|
|
59 | } | |||
|
|
60 | ||||
|
|
61 | private _notify(evt: T) { | |||
|
|
62 | let guard = (observer: IObserver<T>) => { | |||
|
|
63 | try { | |||
|
|
64 | observer(evt); | |||
|
|
65 | } catch (e) { | |||
|
|
66 | this.onObserverException(e); | |||
|
|
67 | } | |||
|
|
68 | } | |||
|
|
69 | ||||
|
|
70 | if (this._once.length) { | |||
|
|
71 | for (let i = 0; i < this._once.length; i++) | |||
|
|
72 | guard(this._once[i]); | |||
|
|
73 | this._once = []; | |||
|
|
74 | } | |||
|
|
75 | ||||
|
|
76 | for (let i = 0; i < this._observers.length; i++) | |||
|
|
77 | guard(this._observers[i]); | |||
|
|
78 | } | |||
|
|
79 | } | |||
|
|
80 | ||||
|
|
81 | namespace Observable { | |||
|
|
82 | export const traceSource = trace; | |||
|
|
83 | } | |||
|
|
84 | ||||
|
|
85 | export = Observable; No newline at end of file | |||
| @@ -0,0 +1,83 | |||||
|
|
1 | import { watchFile } from "fs"; | |||
|
|
2 | ||||
|
|
3 | export interface IDestroyable { | |||
|
|
4 | destroy(); | |||
|
|
5 | } | |||
|
|
6 | ||||
|
|
7 | export interface ICancellation { | |||
|
|
8 | throwIfRequested(): void; | |||
|
|
9 | isRequested(): boolean; | |||
|
|
10 | isSupported(): boolean; | |||
|
|
11 | register(cb: (e: any) => void): void; | |||
|
|
12 | } | |||
|
|
13 | ||||
|
|
14 | /** | |||
|
|
15 | * Интерфейс поддерживающий асинхронную активацию | |||
|
|
16 | */ | |||
|
|
17 | export interface IActivatable { | |||
|
|
18 | /** | |||
|
|
19 | * @returns Boolean indicates the current state | |||
|
|
20 | */ | |||
|
|
21 | isActive(): boolean; | |||
|
|
22 | ||||
|
|
23 | /** | |||
|
|
24 | * Starts the component activation | |||
|
|
25 | * @param ct cancellation token for this operation | |||
|
|
26 | */ | |||
|
|
27 | activate(ct?: ICancellation) : Promise<void>; | |||
|
|
28 | ||||
|
|
29 | /** | |||
|
|
30 | * Starts the component deactivation | |||
|
|
31 | * @param ct cancellation token for this operation | |||
|
|
32 | */ | |||
|
|
33 | deactivate(ct?: ICancellation) : Promise<void>; | |||
|
|
34 | ||||
|
|
35 | /** | |||
|
|
36 | * Sets the activation controller for this component | |||
|
|
37 | * @param controller The activation controller | |||
|
|
38 | * | |||
|
|
39 | * Activation controller checks whether this component | |||
|
|
40 | * can be activated and manages the active state of the | |||
|
|
41 | * component | |||
|
|
42 | */ | |||
|
|
43 | setActivationController(controller: IActivationController); | |||
|
|
44 | ||||
|
|
45 | /** | |||
|
|
46 | * Gets the current activation controller for this component | |||
|
|
47 | */ | |||
|
|
48 | getActivationController(): IActivationController; | |||
|
|
49 | } | |||
|
|
50 | ||||
|
|
51 | export interface IActivationController { | |||
|
|
52 | activating(component: IActivatable, ct?: ICancellation): Promise<void>; | |||
|
|
53 | ||||
|
|
54 | activated(component: IActivatable, ct?: ICancellation): Promise<void>; | |||
|
|
55 | ||||
|
|
56 | deactivating(component: IActivatable, ct?: ICancellation): Promise<void>; | |||
|
|
57 | ||||
|
|
58 | deactivated(component: IActivatable, ct?: ICancellation): Promise<void>; | |||
|
|
59 | ||||
|
|
60 | deactivate(ct?: ICancellation): Promise<void>; | |||
|
|
61 | ||||
|
|
62 | activate(component: IActivatable, ct?: ICancellation): Promise<void>; | |||
|
|
63 | ||||
|
|
64 | getActive(): IActivatable; | |||
|
|
65 | } | |||
|
|
66 | ||||
|
|
67 | export interface IAsyncComponent { | |||
|
|
68 | getCompletion(): Promise<void>; | |||
|
|
69 | } | |||
|
|
70 | ||||
|
|
71 | export interface ICancellable { | |||
|
|
72 | cancel(reason?: any): void; | |||
|
|
73 | } | |||
|
|
74 | ||||
|
|
75 | export interface IObserver<T> { | |||
|
|
76 | (x:T): void; | |||
|
|
77 | } | |||
|
|
78 | ||||
|
|
79 | export interface IObservable<T> { | |||
|
|
80 | on(observer: IObserver<T>): IDestroyable; | |||
|
|
81 | ||||
|
|
82 | wait(ct?: ICancellation) : Promise<T>; | |||
|
|
83 | } No newline at end of file | |||
| @@ -1,95 +1,87 | |||||
| 1 |
import { IActivationController } from '. |
|
1 | import { IActivationController, IActivatable, ICancellation } from '../interfaces'; | |
| 2 | import { IActivatable } from './IActivatable'; |
|
|||
| 3 | import { AsyncComponent } from './AsyncComponent'; |
|
2 | import { AsyncComponent } from './AsyncComponent'; | |
| 4 |
import { |
|
3 | import { Cancellation } from '../Cancellation'; | |
| 5 | import { EmptyCancellation } from '../EmptyCancellation'; |
|
|||
| 6 | import * as TraceSource from '../log/TraceSource'; |
|
4 | import * as TraceSource from '../log/TraceSource'; | |
| 7 |
|
5 | |||
| 8 | type Constructor<T = {}> = new (...args: any[]) => T; |
|
6 | type Constructor<T = {}> = new (...args: any[]) => T; | |
| 9 |
|
7 | |||
| 10 | const log = TraceSource.get('@implab/core/components/ActivatableMixin'); |
|
8 | const log = TraceSource.get('@implab/core/components/ActivatableMixin'); | |
| 11 |
|
9 | |||
| 12 | function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) { |
|
10 | function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) { | |
| 13 | return class extends Base implements IActivatable { |
|
11 | return class extends Base implements IActivatable { | |
| 14 | _controller: IActivationController; |
|
12 | _controller: IActivationController; | |
| 15 |
|
13 | |||
| 16 | _active: boolean; |
|
14 | _active: boolean; | |
| 17 |
|
15 | |||
| 18 | isActive() { |
|
16 | isActive() { | |
| 19 | return this._active; |
|
17 | return this._active; | |
| 20 | } |
|
18 | } | |
| 21 |
|
19 | |||
| 22 | getActivationController() { |
|
20 | getActivationController() { | |
| 23 | return this._controller; |
|
21 | return this._controller; | |
| 24 | } |
|
22 | } | |
| 25 |
|
23 | |||
| 26 | setActivationController(controller: IActivationController) { |
|
24 | setActivationController(controller: IActivationController) { | |
| 27 | this._controller = controller; |
|
25 | this._controller = controller; | |
| 28 | } |
|
26 | } | |
| 29 |
|
27 | |||
| 30 | async onActivating(ct: ICancellation) { |
|
28 | async onActivating(ct: ICancellation) { | |
| 31 | if (this._controller) |
|
29 | if (this._controller) | |
| 32 | await this._controller.activating(this, ct); |
|
30 | await this._controller.activating(this, ct); | |
| 33 | } |
|
31 | } | |
| 34 |
|
32 | |||
| 35 | async onActivated(ct: ICancellation) { |
|
33 | async onActivated(ct: ICancellation) { | |
| 36 | if (this._controller) |
|
34 | if (this._controller) | |
| 37 | await this._controller.activated(this, ct); |
|
35 | await this._controller.activated(this, ct); | |
| 38 | } |
|
36 | } | |
| 39 |
|
37 | |||
| 40 |
|
|
38 | activate(ct: ICancellation = Cancellation.none) { | |
|
|
39 | return this.runOperation(this._activateAsync.bind(this), ct); | |||
|
|
40 | } | |||
|
|
41 | ||||
|
|
42 | async _activateAsync(ct: ICancellation) { | |||
| 41 | if (this.isActive()) |
|
43 | if (this.isActive()) | |
| 42 | return; |
|
44 | return; | |
| 43 | ct = this.startOperation(ct); |
|
45 | ||
|
|
46 | await this.onActivating(ct); | |||
|
|
47 | this._active = true; | |||
| 44 | try { |
|
48 | try { | |
| 45 |
await this.onActivat |
|
49 | await this.onActivated(ct); | |
| 46 | this._active = true; |
|
|||
| 47 | try { |
|
|||
| 48 | await this.onActivated(ct); |
|
|||
| 49 | } catch(e) { |
|
|||
| 50 | log.error("Suppressed onActivated error: {0}", e); |
|
|||
| 51 | } |
|
|||
| 52 | this.completeSuccess(); |
|
|||
| 53 | } catch (e) { |
|
50 | } catch (e) { | |
| 54 | this.completeFail(e); |
|
51 | log.error("Suppressed onActivated error: {0}", e); | |
| 55 | } |
|
52 | } | |
| 56 | return this.getCompletion(); |
|
|||
| 57 | } |
|
53 | } | |
| 58 |
|
54 | |||
| 59 | async onDeactivating(ct: ICancellation) { |
|
55 | async onDeactivating(ct: ICancellation) { | |
| 60 | if (this._controller) |
|
56 | if (this._controller) | |
| 61 | await this._controller.deactivating(this, ct); |
|
57 | await this._controller.deactivating(this, ct); | |
| 62 | } |
|
58 | } | |
| 63 |
|
59 | |||
| 64 | async onDeactivated(ct: ICancellation) { |
|
60 | async onDeactivated(ct: ICancellation) { | |
| 65 | if (this._controller) |
|
61 | if (this._controller) | |
| 66 | await this._controller.deactivated(this, ct); |
|
62 | await this._controller.deactivated(this, ct); | |
| 67 | } |
|
63 | } | |
| 68 |
|
64 | |||
| 69 |
|
|
65 | deactivate(ct: ICancellation = Cancellation.none) { | |
|
|
66 | return this.runOperation(this._deactivateAsync.bind(this), ct); | |||
|
|
67 | } | |||
|
|
68 | ||||
|
|
69 | async _deactivateAsync(ct: ICancellation) { | |||
| 70 | if (!this.isActive()) |
|
70 | if (!this.isActive()) | |
| 71 | return; |
|
71 | return; | |
| 72 |
|
|
72 | await this.onDeactivating(ct); | |
|
|
73 | this._active = false; | |||
| 73 | try { |
|
74 | try { | |
| 74 |
await this.onDeactivat |
|
75 | await this.onDeactivated(ct); | |
| 75 | this._active = false; |
|
|||
| 76 | try { |
|
|||
| 77 | await this.onDeactivated(ct); |
|
|||
| 78 | } catch(e) { |
|
|||
| 79 | log.error("Suppressed onDeactivated error: {0}", e); |
|
|||
| 80 | } |
|
|||
| 81 | this.completeSuccess(); |
|
|||
| 82 | } catch (e) { |
|
76 | } catch (e) { | |
| 83 | this.completeFail(e); |
|
77 | log.error("Suppressed onDeactivated error: {0}", e); | |
| 84 | } |
|
78 | } | |
| 85 | return this.getCompletion(); |
|
|||
| 86 | } |
|
79 | } | |
| 87 |
|
||||
| 88 | } |
|
80 | } | |
| 89 | } |
|
81 | } | |
| 90 |
|
82 | |||
| 91 | namespace ActivatableMixin { |
|
83 | namespace ActivatableMixin { | |
| 92 | export const traceSource = log; |
|
84 | export const traceSource = log; | |
| 93 | } |
|
85 | } | |
| 94 |
|
86 | |||
| 95 | export = ActivatableMixin; No newline at end of file |
|
87 | export = ActivatableMixin; | |
| @@ -1,47 +1,17 | |||||
| 1 |
import { |
|
1 | import { Cancellation } from "../Cancellation"; | |
| 2 |
import { |
|
2 | import { IAsyncComponent, ICancellation } from "../interfaces"; | |
| 3 |
|
3 | |||
| 4 | export class AsyncComponent { |
|
4 | export class AsyncComponent implements IAsyncComponent { | |
| 5 | _completion: Promise<void>; |
|
5 | _completion: Promise<void> = Promise.resolve(); | |
| 6 |
|
||||
| 7 | _deferred: { |
|
|||
| 8 | resolve(): void |
|
|||
| 9 | reject(reason: any): void |
|
|||
| 10 | }; |
|
|||
| 11 |
|
6 | |||
| 12 | getCompletion() { return this._completion }; |
|
7 | getCompletion() { return this._completion }; | |
| 13 |
|
8 | |||
| 14 |
|
|
9 | runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) { | |
| 15 | if (this._deferred) |
|
10 | // TODO create cancellation source here | |
| 16 | throw new Error("The async operation is already pending"); |
|
11 | async function guard() { | |
| 17 |
|
12 | await op(ct); | ||
| 18 | this._completion = new Promise<void>((resolve, reject) => { |
|
13 | } | |
| 19 | this._deferred = { |
|
|||
| 20 | resolve: resolve, |
|
|||
| 21 | reject: reject |
|
|||
| 22 | } |
|
|||
| 23 | }); |
|
|||
| 24 | return ct; |
|
|||
| 25 | } |
|
|||
| 26 |
|
14 | |||
| 27 | completeSuccess() { |
|
15 | return this._completion = guard(); | |
| 28 | this._deferred.resolve(); |
|
|||
| 29 | this._deferred = null; |
|
|||
| 30 | } |
|
|||
| 31 |
|
||||
| 32 | completeFail(reason: any) { |
|
|||
| 33 | this._deferred.reject(reason); |
|
|||
| 34 | this._deferred = null; |
|
|||
| 35 | } |
|
|||
| 36 |
|
||||
| 37 | async runOperation(cb: (ct: ICancellation) => Promise<void>, ct: ICancellation = EmptyCancellation.default) { |
|
|||
| 38 | //safe.argumentNotNull(cb, "cb") |
|
|||
| 39 | ct = this.startOperation(ct); |
|
|||
| 40 | try { |
|
|||
| 41 | await cb(ct); |
|
|||
| 42 | this.completeSuccess(); |
|
|||
| 43 | } catch(e) { |
|
|||
| 44 | this.completeFail(e); |
|
|||
| 45 | } |
|
|||
| 46 | } |
|
16 | } | |
| 47 | } No newline at end of file |
|
17 | } | |
| @@ -1,110 +1,110 | |||||
| 1 | import * as tape from 'tape'; |
|
1 | import * as tape from 'tape'; | |
| 2 | import * as ActivatableMixin from '@implab/core/components/ActivatableMixin'; |
|
2 | import * as ActivatableMixin from '@implab/core/components/ActivatableMixin'; | |
| 3 | import { AsyncComponent } from '@implab/core/components/AsyncComponent'; |
|
3 | import { AsyncComponent } from '@implab/core/components/AsyncComponent'; | |
| 4 | import { IActivationController } from '@implab/core/components/IActivationController'; |
|
4 | import { IActivationController } from '@implab/core/components/IActivationController'; | |
| 5 | import { IActivatable } from '@implab/core/components/IActivatable'; |
|
5 | import { IActivatable } from '@implab/core/components/IActivatable'; | |
| 6 | import { ICancellation } from '@implab/core/ICancellation'; |
|
6 | import { ICancellation } from '@implab/core/ICancellation'; | |
| 7 | import { EmptyCancellation } from '@implab/core/EmptyCancellation'; |
|
7 | import { EmptyCancellation } from '@implab/core/EmptyCancellation'; | |
| 8 |
|
8 | |||
| 9 | class SimpleActivatable extends ActivatableMixin(AsyncComponent) { |
|
9 | class SimpleActivatable extends ActivatableMixin(AsyncComponent) { | |
| 10 |
|
10 | |||
| 11 | } |
|
11 | } | |
| 12 |
|
12 | |||
| 13 | class MockActivationController implements IActivationController { |
|
13 | class MockActivationController implements IActivationController { | |
| 14 |
|
14 | |||
| 15 | _active: IActivatable = null; |
|
15 | _active: IActivatable = null; | |
| 16 |
|
16 | |||
| 17 |
|
17 | |||
| 18 | getActive() : IActivatable { |
|
18 | getActive() : IActivatable { | |
| 19 | return this._active; |
|
19 | return this._active; | |
| 20 | } |
|
20 | } | |
| 21 |
|
21 | |||
| 22 | async deactivate() { |
|
22 | async deactivate() { | |
| 23 | if (this._active) |
|
23 | if (this._active) | |
| 24 | await this._active.deactivate(); |
|
24 | await this._active.deactivate(); | |
| 25 | this._active = null; |
|
25 | this._active = null; | |
| 26 | } |
|
26 | } | |
| 27 |
|
27 | |||
| 28 | async activate(component: IActivatable) { |
|
28 | async activate(component: IActivatable) { | |
| 29 | if (!component || component.isActive()) |
|
29 | if (!component || component.isActive()) | |
| 30 | return; |
|
30 | return; | |
| 31 | component.setActivationController(this); |
|
31 | component.setActivationController(this); | |
| 32 |
|
32 | |||
| 33 | await component.activate(); |
|
33 | await component.activate(); | |
| 34 | } |
|
34 | } | |
| 35 |
|
35 | |||
| 36 | async activating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { |
|
36 | async activating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { | |
| 37 | if (component != this._active) |
|
37 | if (component != this._active) | |
| 38 | await this.deactivate(); |
|
38 | await this.deactivate(); | |
| 39 | } |
|
39 | } | |
| 40 |
|
40 | |||
| 41 | async activated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { |
|
41 | async activated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { | |
| 42 | this._active = component; |
|
42 | this._active = component; | |
| 43 | } |
|
43 | } | |
| 44 |
|
44 | |||
| 45 | async deactivating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { |
|
45 | async deactivating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { | |
| 46 |
|
46 | |||
| 47 | } |
|
47 | } | |
| 48 |
|
48 | |||
| 49 | async deactivated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { |
|
49 | async deactivated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { | |
| 50 | if (this._active == component) |
|
50 | if (this._active == component) | |
| 51 | this._active = null; |
|
51 | this._active = null; | |
| 52 | } |
|
52 | } | |
| 53 | } |
|
53 | } | |
| 54 |
|
54 | |||
| 55 | tape('simple activation',async function(t){ |
|
55 | tape('simple activation',async function(t){ | |
| 56 |
|
56 | |||
| 57 | let a = new SimpleActivatable(); |
|
57 | let a = new SimpleActivatable(); | |
| 58 | t.false(a.isActive()); |
|
58 | t.false(a.isActive()); | |
| 59 |
|
59 | |||
| 60 | await a.activate(); |
|
60 | await a.activate(); | |
| 61 | t.true(a.isActive()); |
|
61 | t.true(a.isActive()); | |
| 62 |
|
62 | |||
| 63 | await a.deactivate(); |
|
63 | await a.deactivate(); | |
| 64 | t.false(a.isActive()); |
|
64 | t.false(a.isActive()); | |
| 65 |
|
65 | |||
| 66 | t.end(); |
|
66 | t.end(); | |
| 67 | }); |
|
67 | }); | |
| 68 |
|
68 | |||
| 69 | tape('controller activation', async function(t) { |
|
69 | tape('controller activation', async function(t) { | |
| 70 |
|
70 | |||
| 71 | let a = new SimpleActivatable(); |
|
71 | let a = new SimpleActivatable(); | |
| 72 | let c = new MockActivationController(); |
|
72 | let c = new MockActivationController(); | |
| 73 |
|
73 | |||
| 74 | t.false(a.isActive(), "the component is not active by default"); |
|
74 | t.false(a.isActive(), "the component is not active by default"); | |
| 75 | t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default"); |
|
75 | t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default"); | |
| 76 | t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default"); |
|
76 | t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default"); | |
| 77 |
|
77 | |||
| 78 | t.comment("Active the component through the controller"); |
|
78 | t.comment("Active the component through the controller"); | |
| 79 | await c.activate(a); |
|
79 | await c.activate(a); | |
| 80 | t.true(a.isActive(), "The component should successfully activate"); |
|
80 | t.true(a.isActive(), "The component should successfully activate"); | |
| 81 |
t. |
|
81 | t.equal(c.getActive(), a, "The controller should point to the activated component"); | |
| 82 |
t. |
|
82 | t.equal(a.getActivationController(), c, "The component should point to the controller"); | |
| 83 |
|
83 | |||
| 84 | t.comment("Deactive the component throug the controller"); |
|
84 | t.comment("Deactive the component throug the controller"); | |
| 85 | await c.deactivate(); |
|
85 | await c.deactivate(); | |
| 86 |
|
86 | |||
| 87 | t.false(a.isActive(), "The component should successfully deactivate"); |
|
87 | t.false(a.isActive(), "The component should successfully deactivate"); | |
| 88 |
t. |
|
88 | t.equal(c.getActive(), null, "The controller shouldn't point to any component"); | |
| 89 |
t. |
|
89 | t.equal(a.getActivationController(), c, "The componet should point to it's controller"); | |
| 90 |
|
90 | |||
| 91 | t.end(); |
|
91 | t.end(); | |
| 92 | }); |
|
92 | }); | |
| 93 |
|
93 | |||
| 94 | tape('handle error in onActivating', async function(t) { |
|
94 | tape('handle error in onActivating', async function(t) { | |
| 95 | let a = new SimpleActivatable(); |
|
95 | let a = new SimpleActivatable(); | |
| 96 |
|
96 | |||
| 97 | a.onActivating = async function() { |
|
97 | a.onActivating = async function() { | |
| 98 | throw "Should fail"; |
|
98 | throw "Should fail"; | |
| 99 | }; |
|
99 | }; | |
| 100 |
|
100 | |||
| 101 | try { |
|
101 | try { | |
| 102 | await a.activate(); |
|
102 | await a.activate(); | |
| 103 | t.fail("activation should fail"); |
|
103 | t.fail("activation should fail"); | |
| 104 | } catch { |
|
104 | } catch { | |
| 105 | } |
|
105 | } | |
| 106 |
|
106 | |||
| 107 | t.false(a.isActive(), "the component should remain inactive"); |
|
107 | t.false(a.isActive(), "the component should remain inactive"); | |
| 108 |
|
108 | |||
| 109 | t.end(); |
|
109 | t.end(); | |
| 110 | }); No newline at end of file |
|
110 | }); | |
| @@ -1,22 +1,44 | |||||
| 1 | import * as TraceSource from '@implab/core/log/TraceSource' |
|
1 | import * as TraceSource from '@implab/core/log/TraceSource' | |
| 2 | import * as tape from 'tape'; |
|
2 | import * as tape from 'tape'; | |
| 3 |
|
3 | |||
| 4 | const sourceId = 'test/TraceSourceTests'; |
|
4 | const sourceId = 'test/TraceSourceTests'; | |
| 5 |
|
5 | |||
| 6 | tape('', t => { |
|
6 | tape('trace message', t => { | |
| 7 | let trace = TraceSource.get(sourceId); |
|
7 | let trace = TraceSource.get(sourceId); | |
| 8 |
|
8 | |||
| 9 | trace.level = TraceSource.DebugLevel; |
|
9 | trace.level = TraceSource.DebugLevel; | |
| 10 |
|
10 | |||
| 11 | let h = trace.on((sender,level,msg) => { |
|
11 | let h = trace.on((sender,level,msg) => { | |
| 12 | t.equal(sender, trace, "sender should be the current trace source"); |
|
12 | t.equal(sender, trace, "sender should be the current trace source"); | |
| 13 | t.equal(TraceSource.DebugLevel, level, "level should be debug level"); |
|
13 | t.equal(TraceSource.DebugLevel, level, "level should be debug level"); | |
| 14 |
t.equal(msg, "Hello, World!", "The message should be formatted |
|
14 | t.equal(msg, "Hello, World!", "The message should be a formatted message"); | |
| 15 |
|
15 | |||
| 16 | t.end(); |
|
16 | t.end(); | |
| 17 | }); |
|
17 | }); | |
| 18 |
|
18 | |||
| 19 | trace.debug("Hello, {0}!", "World"); |
|
19 | trace.debug("Hello, {0}!", "World"); | |
| 20 |
|
20 | |||
| 21 | h.destroy(); |
|
21 | h.destroy(); | |
|
|
22 | }); | |||
|
|
23 | ||||
|
|
24 | tape('trace event', t => { | |||
|
|
25 | let trace = TraceSource.get(sourceId); | |||
|
|
26 | ||||
|
|
27 | trace.level = TraceSource.DebugLevel; | |||
|
|
28 | ||||
|
|
29 | let event = { | |||
|
|
30 | name: "custom event" | |||
|
|
31 | }; | |||
|
|
32 | ||||
|
|
33 | let h = trace.on((sender,level,msg) => { | |||
|
|
34 | t.equal(sender, trace, "sender should be the current trace source"); | |||
|
|
35 | t.equal(TraceSource.DebugLevel, level, "level should be debug level"); | |||
|
|
36 | t.equal(msg, event, "The message should be the specified object"); | |||
|
|
37 | ||||
|
|
38 | t.end(); | |||
|
|
39 | }); | |||
|
|
40 | ||||
|
|
41 | trace.traceEvent(TraceSource.DebugLevel, event); | |||
|
|
42 | ||||
|
|
43 | h.destroy(); | |||
| 22 | }); No newline at end of file |
|
44 | }); | |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now
