Skip to content

Commit

Permalink
Merge pull request #1883 from blesh/sequenceEqual
Browse files Browse the repository at this point in the history
feat(sequenceEqual): adds sequenceEqual operator
  • Loading branch information
kwonoj authored Aug 16, 2016
2 parents 64ecb9e + ec887f5 commit da8c1c2
Show file tree
Hide file tree
Showing 6 changed files with 538 additions and 0 deletions.
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

0 comments on commit da8c1c2

Please sign in to comment.