Skip to content

Commit

Permalink
feat(stats): Add basic stats plugin.
Browse files Browse the repository at this point in the history
  • Loading branch information
cartant committed Nov 6, 2017
1 parent 5257f69 commit a49344c
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 1 deletion.
1 change: 1 addition & 0 deletions scripts/bundle-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const shims = {
"rxjs/add/observable/interval": "Rx.unused",
"rxjs/add/observable/never": "Rx.unused",
"rxjs/add/observable/of": "Rx.unused",
"rxjs/add/observable/timer": "Rx.unused",
"rxjs/add/operator/dematerialize": "Rx.unused",
"rxjs/add/operator/let": "Rx.unused",
"rxjs/add/operator/map": "Rx.unused",
Expand Down
1 change: 1 addition & 0 deletions source/index-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ import "./plugin/log-plugin-spec";
import "./plugin/pause-plugin-spec";
import "./plugin/snapshot-plugin-spec";
import "./plugin/stack-trace-plugin-spec";
import "./plugin/stats-plugin-spec";
import "./spy-spec";
1 change: 1 addition & 0 deletions source/plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from "./pause-plugin";
export * from "./plugin";
export * from "./snapshot-plugin";
export * from "./stack-trace-plugin";
export * from "./stats-plugin";
119 changes: 119 additions & 0 deletions source/plugin/stats-plugin-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* @license Copyright © 2017 Nicholas Jamieson. All Rights Reserved.
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/cartant/rxjs-spy
*/
/*tslint:disable:no-unused-expression*/

import { expect } from "chai";
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import { StatsPlugin } from "./stats-plugin";
import { spy } from "../spy";

import "rxjs/add/observable/timer";

describe("StatsPlugin", () => {

let statsPlugin: StatsPlugin;
let teardown: () => void;

afterEach(() => {

if (teardown) {
teardown();
}
});

beforeEach(() => {

statsPlugin = new StatsPlugin();
teardown = spy({ plugins: [statsPlugin], warning: false });
});

it("should count subscribes/unsubscribes", () => {

const subject = new Subject<number>();

let stats = statsPlugin.stats;
expect(stats.subscribes).to.equal(0);
expect(stats.unsubscribes).to.equal(0);

const subscription = subject.subscribe();

stats = statsPlugin.stats;
expect(stats.subscribes).to.equal(1);
expect(stats.unsubscribes).to.equal(0);

subscription.unsubscribe();

stats = statsPlugin.stats;
expect(stats.subscribes).to.equal(1);
expect(stats.unsubscribes).to.equal(1);
});

it("should count completes", () => {

const subject = new Subject<number>();
const subscription = subject.subscribe();

let stats = statsPlugin.stats;
expect(stats.completes).to.equal(0);

subject.complete();

stats = statsPlugin.stats;
expect(stats.completes).to.equal(1);
});

it("should count errors", () => {

const subject = new Subject<number>();
const subscription = subject.subscribe();

let stats = statsPlugin.stats;
expect(stats.errors).to.equal(0);

subject.error(new Error("Boom!"));

stats = statsPlugin.stats;
expect(stats.errors).to.equal(1);
});

it("should count nexts", () => {

const subject = new Subject<number>();
const subscription = subject.subscribe();

let stats = statsPlugin.stats;
expect(stats.nexts).to.equal(0);

subject.next(1);

stats = statsPlugin.stats;
expect(stats.nexts).to.equal(1);
});

it("should include the tick", () => {

const subject = new Subject<number>();
const subscription = subject.subscribe();

subject.next(1);

const stats = statsPlugin.stats;
expect(stats.tick).to.not.equal(0);
});

it("should determine the timespan between the first and last notification", (callback: any) => {

Observable.timer(10).subscribe(
() => {
const stats = statsPlugin.stats;
expect(stats.timespan).to.not.be.below(10);
},
callback,
callback
);
});
});
79 changes: 79 additions & 0 deletions source/plugin/stats-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* @license Copyright © 2017 Nicholas Jamieson. All Rights Reserved.
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/cartant/rxjs-spy
*/

import { Observable } from "rxjs/Observable";
import { Subscriber } from "rxjs/Subscriber";
import { BasePlugin, Notification, SubscriberRef, SubscriptionRef } from "./plugin";
import { tick } from "../tick";

export interface Stats {
completes: number;
errors: number;
nexts: number;
subscribes: number;
tick: number;
timespan: number;
unsubscribes: number;
}

export class StatsPlugin extends BasePlugin {

private stats_: Stats = {
completes: 0,
errors: 0,
nexts: 0,
subscribes: 0,
tick: 0,
timespan: 0,
unsubscribes: 0
};
private time_ = 0;

beforeComplete(ref: SubscriptionRef): void {
const { stats_ } = this;
++stats_.completes;
this.all_();
}

beforeError(ref: SubscriptionRef, error: any): void {
const { stats_ } = this;
++stats_.errors;
this.all_();
}

beforeNext(ref: SubscriptionRef, value: any): void {
const { stats_ } = this;
++stats_.nexts;
this.all_();
}

beforeSubscribe(ref: SubscriberRef): void {
const { stats_ } = this;
++stats_.subscribes;
this.all_();
}

beforeUnsubscribe(ref: SubscriptionRef): void {
const { stats_ } = this;
++stats_.unsubscribes;
this.all_();
}

public get stats(): Stats {
const { stats_ } = this;
return { ...stats_ };
}

private all_(): void {
const { stats_, time_ } = this;
if (time_ === 0) {
this.time_ = Date.now();
} else {
stats_.timespan = Date.now() - time_;
}
stats_.tick = tick();
}
}
23 changes: 22 additions & 1 deletion source/spy-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import * as sinon from "sinon";
import { Plugin } from "./plugin";
import { flush, _let, log, pause, show, spy } from "./spy";
import { flush, _let, log, pause, show, spy, stats } from "./spy";
import { tick } from "./tick";

import "rxjs/add/operator/mapTo";
Expand Down Expand Up @@ -347,6 +347,27 @@ describe("spy", () => {
});
});

describe("stats", () => {

it("should show the stats", () => {

teardown = spy({ ...options });

const calls: any[][] = [];
const subject = new Subject<number>();
const subscription = subject.subscribe();

stats({
log(...args: any[]): void { calls.push(args); }
});

expect(calls).to.not.be.empty;
expect(calls[0]).to.deep.equal(["Stats"]);
expect(calls[1]).to.deep.equal([" subscribes =", 1]);
expect(calls[2]).to.deep.equal([" unsubscribes =", 0]);
});
});

describe("tick", () => {

it("should increment with each subscription and value, etc.", () => {
Expand Down
30 changes: 30 additions & 0 deletions source/spy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
Plugin,
SnapshotPlugin,
StackTracePlugin,
Stats,
StatsPlugin,
SubscriberSnapshot,
SubscriptionRef,
SubscriptionSnapshot
Expand Down Expand Up @@ -102,6 +104,11 @@ if (typeof window !== "undefined") {
spy.apply(null, args);
},

stats(): void {

stats();
},

undo(...args: any[]): void {

if (args.length === 0) {
Expand Down Expand Up @@ -295,6 +302,7 @@ export function spy(options: {
plugins_ = plugins;
} else {
plugins_ = [
new StatsPlugin(),
new StackTracePlugin(options as { [key: string]: any }),
new GraphPlugin(options as { [key: string]: any }),
new SnapshotPlugin(options as { [key: string]: any })
Expand All @@ -320,6 +328,28 @@ export function spy(options: {
return teardown;
}

export function stats(partialLogger?: PartialLogger): void {

const statsPlugin = find(StatsPlugin);
if (!statsPlugin) {
/*tslint:disable-next-line:no-console*/
console.warn("Stats are not enabled.");
return;
}

const stats = statsPlugin.stats;
const logger = toLogger(partialLogger || defaultLogger);
logger.group("Stats");
logger.log("subscribes =", stats.subscribes);
logger.log("unsubscribes =", stats.unsubscribes);
logger.log("nexts =", stats.nexts);
logger.log("errors =", stats.errors);
logger.log("completes =", stats.completes);
logger.log("tick =", stats.tick);
logger.log("timespan =", stats.timespan);
logger.groupEnd();
}

export function subscribeWithoutSpy(this: Observable<any>, ...args: any[]): Subscription {

const subscribePrevious = Observable.prototype.subscribe;
Expand Down

0 comments on commit a49344c

Please sign in to comment.