| @@ -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 '. |
|
|
|
2 | import { IActivatable } from './IActivatable'; | |
|
|
1 | import { IActivationController, IActivatable, ICancellation } from '../interfaces'; | |
|
|
3 | 2 | import { AsyncComponent } from './AsyncComponent'; |
|
|
4 |
import { |
|
|
|
5 | import { EmptyCancellation } from '../EmptyCancellation'; | |
|
|
3 | import { Cancellation } from '../Cancellation'; | |
|
|
6 | 4 | import * as TraceSource from '../log/TraceSource'; |
|
|
7 | 5 | |
|
|
8 | 6 | type Constructor<T = {}> = new (...args: any[]) => T; |
|
|
9 | 7 | |
|
|
10 | 8 | const log = TraceSource.get('@implab/core/components/ActivatableMixin'); |
|
|
11 | 9 | |
|
|
12 | 10 | function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) { |
|
|
13 | 11 | return class extends Base implements IActivatable { |
|
|
14 | 12 | _controller: IActivationController; |
|
|
15 | 13 | |
|
|
16 | 14 | _active: boolean; |
|
|
17 | 15 | |
|
|
18 | 16 | isActive() { |
|
|
19 | 17 | return this._active; |
|
|
20 | 18 | } |
|
|
21 | 19 | |
|
|
22 | 20 | getActivationController() { |
|
|
23 | 21 | return this._controller; |
|
|
24 | 22 | } |
|
|
25 | 23 | |
|
|
26 | 24 | setActivationController(controller: IActivationController) { |
|
|
27 | 25 | this._controller = controller; |
|
|
28 | 26 | } |
|
|
29 | 27 | |
|
|
30 | 28 | async onActivating(ct: ICancellation) { |
|
|
31 | 29 | if (this._controller) |
|
|
32 | 30 | await this._controller.activating(this, ct); |
|
|
33 | 31 | } |
|
|
34 | 32 | |
|
|
35 | 33 | async onActivated(ct: ICancellation) { |
|
|
36 | 34 | if (this._controller) |
|
|
37 | 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 | 43 | if (this.isActive()) |
|
|
42 | 44 | return; |
|
|
43 | ct = this.startOperation(ct); | |
|
|
45 | ||
|
|
46 | await this.onActivating(ct); | |
|
|
47 | this._active = true; | |
|
|
44 | 48 | try { |
|
|
45 |
await this.onActivat |
|
|
|
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(); | |
|
|
49 | await this.onActivated(ct); | |
|
|
53 | 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 | 55 | async onDeactivating(ct: ICancellation) { |
|
|
60 | 56 | if (this._controller) |
|
|
61 | 57 | await this._controller.deactivating(this, ct); |
|
|
62 | 58 | } |
|
|
63 | 59 | |
|
|
64 | 60 | async onDeactivated(ct: ICancellation) { |
|
|
65 | 61 | if (this._controller) |
|
|
66 | 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 | 70 | if (!this.isActive()) |
|
|
71 | 71 | return; |
|
|
72 |
|
|
|
|
72 | await this.onDeactivating(ct); | |
|
|
73 | this._active = false; | |
|
|
73 | 74 | try { |
|
|
74 |
await this.onDeactivat |
|
|
|
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(); | |
|
|
75 | await this.onDeactivated(ct); | |
|
|
82 | 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 | 83 | namespace ActivatableMixin { |
|
|
92 | 84 | export const traceSource = log; |
|
|
93 | 85 | } |
|
|
94 | 86 | |
|
|
95 | 87 | export = ActivatableMixin; No newline at end of file |
| @@ -1,47 +1,17 | |||
|
|
1 |
import { |
|
|
|
2 |
import { |
|
|
|
1 | import { Cancellation } from "../Cancellation"; | |
|
|
2 | import { IAsyncComponent, ICancellation } from "../interfaces"; | |
|
|
3 | 3 | |
|
|
4 | export class AsyncComponent { | |
|
|
5 | _completion: Promise<void>; | |
|
|
6 | ||
|
|
7 | _deferred: { | |
|
|
8 | resolve(): void | |
|
|
9 | reject(reason: any): void | |
|
|
10 | }; | |
|
|
4 | export class AsyncComponent implements IAsyncComponent { | |
|
|
5 | _completion: Promise<void> = Promise.resolve(); | |
|
|
11 | 6 | |
|
|
12 | 7 | getCompletion() { return this._completion }; |
|
|
13 | 8 | |
|
|
14 |
|
|
|
|
15 | if (this._deferred) | |
|
|
16 | throw new Error("The async operation is already pending"); | |
|
|
17 | ||
|
|
18 | this._completion = new Promise<void>((resolve, reject) => { | |
|
|
19 | this._deferred = { | |
|
|
20 | resolve: resolve, | |
|
|
21 | reject: reject | |
|
|
22 | } | |
|
|
23 | }); | |
|
|
24 | return ct; | |
|
|
25 | } | |
|
|
9 | runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) { | |
|
|
10 | // TODO create cancellation source here | |
|
|
11 | async function guard() { | |
|
|
12 | await op(ct); | |
|
|
13 | } | |
|
|
26 | 14 | |
|
|
27 | completeSuccess() { | |
|
|
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 | } | |
|
|
15 | return this._completion = guard(); | |
|
|
46 | 16 | } |
|
|
47 | 17 | } No newline at end of file |
| @@ -1,110 +1,110 | |||
|
|
1 | 1 | import * as tape from 'tape'; |
|
|
2 | 2 | import * as ActivatableMixin from '@implab/core/components/ActivatableMixin'; |
|
|
3 | 3 | import { AsyncComponent } from '@implab/core/components/AsyncComponent'; |
|
|
4 | 4 | import { IActivationController } from '@implab/core/components/IActivationController'; |
|
|
5 | 5 | import { IActivatable } from '@implab/core/components/IActivatable'; |
|
|
6 | 6 | import { ICancellation } from '@implab/core/ICancellation'; |
|
|
7 | 7 | import { EmptyCancellation } from '@implab/core/EmptyCancellation'; |
|
|
8 | 8 | |
|
|
9 | 9 | class SimpleActivatable extends ActivatableMixin(AsyncComponent) { |
|
|
10 | 10 | |
|
|
11 | 11 | } |
|
|
12 | 12 | |
|
|
13 | 13 | class MockActivationController implements IActivationController { |
|
|
14 | 14 | |
|
|
15 | 15 | _active: IActivatable = null; |
|
|
16 | 16 | |
|
|
17 | 17 | |
|
|
18 | 18 | getActive() : IActivatable { |
|
|
19 | 19 | return this._active; |
|
|
20 | 20 | } |
|
|
21 | 21 | |
|
|
22 | 22 | async deactivate() { |
|
|
23 | 23 | if (this._active) |
|
|
24 | 24 | await this._active.deactivate(); |
|
|
25 | 25 | this._active = null; |
|
|
26 | 26 | } |
|
|
27 | 27 | |
|
|
28 | 28 | async activate(component: IActivatable) { |
|
|
29 | 29 | if (!component || component.isActive()) |
|
|
30 | 30 | return; |
|
|
31 | 31 | component.setActivationController(this); |
|
|
32 | 32 | |
|
|
33 | 33 | await component.activate(); |
|
|
34 | 34 | } |
|
|
35 | 35 | |
|
|
36 | 36 | async activating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { |
|
|
37 | 37 | if (component != this._active) |
|
|
38 | 38 | await this.deactivate(); |
|
|
39 | 39 | } |
|
|
40 | 40 | |
|
|
41 | 41 | async activated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { |
|
|
42 | 42 | this._active = component; |
|
|
43 | 43 | } |
|
|
44 | 44 | |
|
|
45 | 45 | async deactivating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { |
|
|
46 | 46 | |
|
|
47 | 47 | } |
|
|
48 | 48 | |
|
|
49 | 49 | async deactivated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) { |
|
|
50 | 50 | if (this._active == component) |
|
|
51 | 51 | this._active = null; |
|
|
52 | 52 | } |
|
|
53 | 53 | } |
|
|
54 | 54 | |
|
|
55 | 55 | tape('simple activation',async function(t){ |
|
|
56 | 56 | |
|
|
57 | 57 | let a = new SimpleActivatable(); |
|
|
58 | 58 | t.false(a.isActive()); |
|
|
59 | 59 | |
|
|
60 | 60 | await a.activate(); |
|
|
61 | 61 | t.true(a.isActive()); |
|
|
62 | 62 | |
|
|
63 | 63 | await a.deactivate(); |
|
|
64 | 64 | t.false(a.isActive()); |
|
|
65 | 65 | |
|
|
66 | 66 | t.end(); |
|
|
67 | 67 | }); |
|
|
68 | 68 | |
|
|
69 | 69 | tape('controller activation', async function(t) { |
|
|
70 | 70 | |
|
|
71 | 71 | let a = new SimpleActivatable(); |
|
|
72 | 72 | let c = new MockActivationController(); |
|
|
73 | 73 | |
|
|
74 | 74 | t.false(a.isActive(), "the component is not active by default"); |
|
|
75 | 75 | t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default"); |
|
|
76 | 76 | t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default"); |
|
|
77 | 77 | |
|
|
78 | 78 | t.comment("Active the component through the controller"); |
|
|
79 | 79 | await c.activate(a); |
|
|
80 | 80 | t.true(a.isActive(), "The component should successfully activate"); |
|
|
81 |
t. |
|
|
|
82 |
t. |
|
|
|
81 | t.equal(c.getActive(), a, "The controller should point to the activated component"); | |
|
|
82 | t.equal(a.getActivationController(), c, "The component should point to the controller"); | |
|
|
83 | 83 | |
|
|
84 | 84 | t.comment("Deactive the component throug the controller"); |
|
|
85 | 85 | await c.deactivate(); |
|
|
86 | 86 | |
|
|
87 | 87 | t.false(a.isActive(), "The component should successfully deactivate"); |
|
|
88 |
t. |
|
|
|
89 |
t. |
|
|
|
88 | t.equal(c.getActive(), null, "The controller shouldn't point to any component"); | |
|
|
89 | t.equal(a.getActivationController(), c, "The componet should point to it's controller"); | |
|
|
90 | 90 | |
|
|
91 | 91 | t.end(); |
|
|
92 | 92 | }); |
|
|
93 | 93 | |
|
|
94 | 94 | tape('handle error in onActivating', async function(t) { |
|
|
95 | 95 | let a = new SimpleActivatable(); |
|
|
96 | 96 | |
|
|
97 | 97 | a.onActivating = async function() { |
|
|
98 | 98 | throw "Should fail"; |
|
|
99 | 99 | }; |
|
|
100 | 100 | |
|
|
101 | 101 | try { |
|
|
102 | 102 | await a.activate(); |
|
|
103 | 103 | t.fail("activation should fail"); |
|
|
104 | 104 | } catch { |
|
|
105 | 105 | } |
|
|
106 | 106 | |
|
|
107 | 107 | t.false(a.isActive(), "the component should remain inactive"); |
|
|
108 | 108 | |
|
|
109 | 109 | t.end(); |
|
|
110 | 110 | }); No newline at end of file |
| @@ -1,22 +1,44 | |||
|
|
1 | 1 | import * as TraceSource from '@implab/core/log/TraceSource' |
|
|
2 | 2 | import * as tape from 'tape'; |
|
|
3 | 3 | |
|
|
4 | 4 | const sourceId = 'test/TraceSourceTests'; |
|
|
5 | 5 | |
|
|
6 | tape('', t => { | |
|
|
6 | tape('trace message', t => { | |
|
|
7 | 7 | let trace = TraceSource.get(sourceId); |
|
|
8 | 8 | |
|
|
9 | 9 | trace.level = TraceSource.DebugLevel; |
|
|
10 | 10 | |
|
|
11 | 11 | let h = trace.on((sender,level,msg) => { |
|
|
12 | 12 | t.equal(sender, trace, "sender should be the current trace source"); |
|
|
13 | 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 | 16 | t.end(); |
|
|
17 | 17 | }); |
|
|
18 | 18 | |
|
|
19 | 19 | trace.debug("Hello, {0}!", "World"); |
|
|
20 | 20 | |
|
|
21 | 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 | 44 | }); No newline at end of file |
|
|
1 | NO CONTENT: file was removed |
|
|
1 | NO CONTENT: file was removed |
|
|
1 | NO CONTENT: file was removed |
|
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now
