Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sequenceEqual): adds sequenceEqual operator #1883

Merged
merged 7 commits into from
Aug 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions perf/micro/immediate-scheduler/operators/sequenceEqual-comparor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var RxOld = require('rx');
var RxNew = require('../../../../index');

module.exports = function (suite) {
function comparor(a, b) {
return a.value === b.value;
}

var values = [1, 2, 3, 4, 5, 6, 7, 8].map(function (x) { return { value: x }; });

var _old = RxOld.Observable.of.apply(null, values.concat(RxOld.Scheduler.immediate))
.sequenceEqual(RxOld.Observable.of.apply(null, values.concat(RxOld.Scheduler.immediate)), comparor);
var _new = RxNew.Observable.of.apply(null, values).sequenceEqual(RxNew.Observable.of.apply(null, values), comparor);

function _next(x) { }
function _error(e) { }
function _complete() { }
return suite
.add('old sequenceEqual with comparor with immediate scheduler', function () {
_old.subscribe(_next, _error, _complete);
})
.add('new sequenceEqual with comparor with immediate scheduler', function () {
_new.subscribe(_next, _error, _complete);
});
};
19 changes: 19 additions & 0 deletions perf/micro/immediate-scheduler/operators/sequenceEqual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var RxOld = require('rx');
var RxNew = require('../../../../index');

module.exports = function (suite) {
var _old = RxOld.Observable.range(0, 25, RxOld.Scheduler.immediate)
.sequenceEqual(RxOld.Observable.range(0, 25, RxOld.Scheduler.immediate));
var _new = RxNew.Observable.range(0, 25).sequenceEqual(RxNew.Observable.range(0, 25));

function _next(x) { }
function _error(e) { }
function _complete() { }
return suite
.add('old sequenceEqual with immediate scheduler', function () {
_old.subscribe(_next, _error, _complete);
})
.add('new sequenceEqual with immediate scheduler', function () {
_new.subscribe(_next, _error, _complete);
});
};
317 changes: 317 additions & 0 deletions spec/operators/sequenceEqual-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
declare const {rxTestScheduler, time, type};

const booleans = { T: true, F: false };

/** @test {sequenceEqual} */
describe('Observable.prototype.sequenceEqual', () => {
asDiagram('sequenceEqual(observable)')('should return true for two equal sequences', () => {
const s1 = hot('--a--^--b--c--d--e--f--g--|');
const s1subs = '^ !';
const s2 = hot('-----^-----b--c--d-e-f------g-|');
const s2subs = '^ !';
const expected = '-------------------------(T|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should return false for two sync observables that are unequal in length', () => {
const s1 = cold('(abcdefg|)');
const s2 = cold('(abc|)');
const expected = '(F|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
});

it('should return true for two sync observables that match', () => {
const s1 = cold('(abcdefg|)');
const s2 = cold('(abcdefg|)');
const expected = '(T|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
});

it('should return true for two observables that match when the last one emits and completes in the same frame', () => {
const s1 = hot('--a--^--b--c--d--e--f--g--|');
const s1subs = '^ !';
const s2 = hot('-----^--b--c--d--e--f--g------|');
const s2subs = '^ !';
const expected = '-------------------------(T|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should return true for two observables that match when the last one emits and completes in the same frame', () => {
const s1 = hot('--a--^--b--c--d--e--f--g--|');
const s1subs = '^ !';
const s2 = hot('-----^--b--c--d--e--f---------(g|)');
const s2subs = '^ !';
const expected = '-------------------------(T|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should error with an errored source', () => {
const s1 = hot('--a--^--b---c---#');
const s2 = hot('--a--^--b---c-----|');
const expected = '-----------#';
const sub = '^ !';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(sub);
expectSubscriptions(s2.subscriptions).toBe(sub);
});

it('should error with an errored compareTo', () => {
const s1 = hot('--a--^--b---c-----|');
const s2 = hot('--a--^--b---c---#');
const expected = '-----------#';
const sub = '^ !';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(sub);
expectSubscriptions(s2.subscriptions).toBe(sub);
});

it('should error if the source is a throw', () => {
const s1 = cold('#'); // throw
const s2 = cold('---a--b--c--|');
const expected = '#'; // throw

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected);
});

it('should never return if source is a never', () => {
const s1 = cold('------------'); // never
const s2 = cold('--a--b--c--|');
const expected = '------------'; // never

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected);
});

it('should never return if compareTo is a never', () => {
const s1 = cold('--a--b--c--|');
const s2 = cold('------------'); // never
const expected = '------------'; // never

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected);
});

it('should return false if source is empty and compareTo is not', () => {
const s1 = cold('|'); // empty
const s2 = cold('------a------');
const expected = '------(F|)';
const subs = '^ !';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(subs);
expectSubscriptions(s2.subscriptions).toBe(subs);
});

it('should return false if compareTo is empty and source is not', () => {
const s1 = cold('------a------');
const s2 = cold('|'); // empty
const expected = '------(F|)';
const subs = '^ !';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(subs);
expectSubscriptions(s2.subscriptions).toBe(subs);
});

it('should return never if compareTo is empty and source is never', () => {
const s1 = cold('-');
const s2 = cold('|');
const expected = '-';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected);
});

it('should return never if source is empty and compareTo is never', () => {
const s1 = cold('|');
const s2 = cold('-');
const expected = '-';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected);
});

it('should error if the comparor errors', () => {
const s1 = hot('--a--^--b-----c------d--|');
const s1subs = '^ !';
const s2 = hot('-----^--------x---y---z-------|');
const s2subs = '^ !';
const expected = '-------------#';

let i = 0;
const source = s1.sequenceEqual(s2, (a: any, b: any) => {
if (++i === 2) {
throw new Error('shazbot');
}
return a.value === b.value;
});

const values = {
a: null,
b: { value: 'bees knees' },
c: { value: 'carpy dumb' },
d: { value: 'derp' },
x: { value: 'bees knees', foo: 'lol' },
y: { value: 'carpy dumb', scooby: 'doo' },
z: { value: 'derp', weCouldBe: 'dancin, yeah' }
};

expectObservable(source).toBe(expected, Object.assign({}, booleans, values), new Error('shazbot'));
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should use the provided comparor', () => {
const s1 = hot('--a--^--b-----c------d--|');
const s1subs = '^ !';
const s2 = hot('-----^--------x---y---z-------|');
const s2subs = '^ !';
const expected = '-------------------------(T|)';

const source = s1.sequenceEqual(s2, (a: any, b: any) => a.value === b.value);

const values = {
a: null,
b: { value: 'bees knees' },
c: { value: 'carpy dumb' },
d: { value: 'derp' },
x: { value: 'bees knees', foo: 'lol' },
y: { value: 'carpy dumb', scooby: 'doo' },
z: { value: 'derp', weCouldBe: 'dancin, yeah' }
};

expectObservable(source).toBe(expected, Object.assign({}, booleans, values));
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should return false for two unequal sequences, compareTo finishing last', () => {
const s1 = hot('--a--^--b--c--d--e--f--g--|');
const s1subs = '^ !';
const s2 = hot('-----^-----b--c--d-e-f------z-|');
const s2subs = '^ !';
const expected = '-----------------------(F|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should return false for two unequal sequences, early wrong value from source', () => {
const s1 = hot('--a--^--b--c---x-----------|');
const s1subs = '^ !';
const s2 = hot('-----^--b--c--d--e--f--|');
const s2subs = '^ !';
const expected = '----------(F|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should return false when the source emits an extra value after the compareTo completes', () => {
const s1 = hot('--a--^--b--c--d--e--f--g--h--|');
const s1subs = '^ !';
const s2 = hot('-----^--b--c--d-|');
const s2subs = '^ !';
const expected = '------------(F|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should return false when the compareTo emits an extra value after the source completes', () => {
const s1 = hot('--a--^--b--c--d-|');
const s1subs = '^ !';
const s2 = hot('-----^--b--c--d--e--f--g--h--|');
const s2subs = '^ !';
const expected = '------------(F|)';

const source = s1.sequenceEqual(s2);

expectObservable(source).toBe(expected, booleans);
expectSubscriptions(s1.subscriptions).toBe(s1subs);
expectSubscriptions(s2.subscriptions).toBe(s2subs);
});

it('should return true for two empty observables', () => {
const s1 = cold('|');
const s2 = cold('|');
const expected = '(T|)';

const source = s1.sequenceEqual(s2);
expectObservable(source).toBe(expected, booleans);
});

it('should return false for an empty observable and an observable that emits', () => {
const s1 = cold('|');
const s2 = cold('---a--|');
const expected = '---(F|)';

const source = s1.sequenceEqual(s2);
expectObservable(source).toBe(expected, booleans);
});

it('should return compare hot and cold observables', () => {
const s1 = hot('---a--^---b---c---d---e---f---g---h---i---j---|');
const s2 = cold( '----b---c-|');
const expected1 = '------------(F|)';
const subs1 = '^ !';
const delay = '-------------------|';
const s3 = cold( '-f---g---h---i---j---|');
const expected2 = ' ---------------------(T|)';
const subs2 = ' ^ !';

const test1 = s1.sequenceEqual(s2);
const test2 = s1.sequenceEqual(s3);

expectObservable(test1).toBe(expected1, booleans);
rxTestScheduler.schedule(() => expectObservable(test2).toBe(expected2, booleans), time(delay));
expectSubscriptions(s2.subscriptions).toBe(subs1);
expectSubscriptions(s3.subscriptions).toBe(subs2);
});
});
1 change: 1 addition & 0 deletions src/Rx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ import './add/operator/retryWhen';
import './add/operator/sample';
import './add/operator/sampleTime';
import './add/operator/scan';
import './add/operator/sequenceEqual';
import './add/operator/share';
import './add/operator/single';
import './add/operator/skip';
Expand Down
11 changes: 11 additions & 0 deletions src/add/operator/sequenceEqual.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

import {Observable} from '../../Observable';
import {sequenceEqual, SequenceEqualSignature} from '../../operator/sequenceEqual';

Observable.prototype.sequenceEqual = sequenceEqual;

declare module '../../Observable' {
interface Observable<T> {
sequenceEqual: SequenceEqualSignature<T>;
}
}
Loading