rx.core.testing.js 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187
  1. // Copyright (c) Microsoft, All rights reserved. See License.txt in the project root for license information.
  2. ;(function (factory) {
  3. var objectTypes = {
  4. 'function': true,
  5. 'object': true
  6. };
  7. function checkGlobal(value) {
  8. return (value && value.Object === Object) ? value : null;
  9. }
  10. var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType) ? exports : null;
  11. var freeModule = (objectTypes[typeof module] && module && !module.nodeType) ? module : null;
  12. var freeGlobal = checkGlobal(freeExports && freeModule && typeof global === 'object' && global);
  13. var freeSelf = checkGlobal(objectTypes[typeof self] && self);
  14. var freeWindow = checkGlobal(objectTypes[typeof window] && window);
  15. var moduleExports = (freeModule && freeModule.exports === freeExports) ? freeExports : null;
  16. var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
  17. var root = freeGlobal || ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) || freeSelf || thisGlobal || Function('return this')();
  18. // Because of build optimizers
  19. if (typeof define === 'function' && define.amd) {
  20. define(['./rx.core'], function (Rx, exports) {
  21. return factory(root, exports, Rx);
  22. });
  23. } else if (typeof module === 'object' && module && module.exports === freeExports) {
  24. module.exports = factory(root, module.exports, require('./rx.core'));
  25. } else {
  26. root.Rx = factory(root, {}, root.Rx);
  27. }
  28. }.call(this, function (root, exp, Rx, undefined) {
  29. // Defaults
  30. var Observer = Rx.Observer,
  31. Observable = Rx.Observable,
  32. Disposable = Rx.Disposable,
  33. disposableEmpty = Disposable.empty,
  34. disposableCreate = Disposable.create,
  35. CompositeDisposable = Rx.CompositeDisposable,
  36. SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
  37. Scheduler = Rx.Scheduler,
  38. ScheduledItem = Rx.internals.ScheduledItem,
  39. SchedulePeriodicRecursive = Rx.internals.SchedulePeriodicRecursive,
  40. PriorityQueue = Rx.internals.PriorityQueue,
  41. inherits = Rx.internals.inherits,
  42. notImplemented = Rx.helpers.notImplemented,
  43. defaultComparer = Rx.helpers.defaultComparer = function (a, b) { return isEqual(a, b); };
  44. /**
  45. * Represents a notification to an observer.
  46. */
  47. var Notification = Rx.Notification = (function () {
  48. function Notification() {
  49. }
  50. Notification.prototype._accept = function (onNext, onError, onCompleted) {
  51. throw new NotImplementedError();
  52. };
  53. Notification.prototype._acceptObserver = function (onNext, onError, onCompleted) {
  54. throw new NotImplementedError();
  55. };
  56. /**
  57. * Invokes the delegate corresponding to the notification or the observer's method corresponding to the notification and returns the produced result.
  58. * @param {Function | Observer} observerOrOnNext Function to invoke for an OnNext notification or Observer to invoke the notification on..
  59. * @param {Function} onError Function to invoke for an OnError notification.
  60. * @param {Function} onCompleted Function to invoke for an OnCompleted notification.
  61. * @returns {Any} Result produced by the observation.
  62. */
  63. Notification.prototype.accept = function (observerOrOnNext, onError, onCompleted) {
  64. return observerOrOnNext && typeof observerOrOnNext === 'object' ?
  65. this._acceptObserver(observerOrOnNext) :
  66. this._accept(observerOrOnNext, onError, onCompleted);
  67. };
  68. /**
  69. * Returns an observable sequence with a single notification.
  70. *
  71. * @memberOf Notifications
  72. * @param {Scheduler} [scheduler] Scheduler to send out the notification calls on.
  73. * @returns {Observable} The observable sequence that surfaces the behavior of the notification upon subscription.
  74. */
  75. Notification.prototype.toObservable = function (scheduler) {
  76. var self = this;
  77. isScheduler(scheduler) || (scheduler = immediateScheduler);
  78. return new AnonymousObservable(function (o) {
  79. return scheduler.schedule(self, function (_, notification) {
  80. notification._acceptObserver(o);
  81. notification.kind === 'N' && o.onCompleted();
  82. });
  83. });
  84. };
  85. return Notification;
  86. })();
  87. var OnNextNotification = (function (__super__) {
  88. inherits(OnNextNotification, __super__);
  89. function OnNextNotification(value) {
  90. this.value = value;
  91. this.kind = 'N';
  92. }
  93. OnNextNotification.prototype._accept = function (onNext) {
  94. return onNext(this.value);
  95. };
  96. OnNextNotification.prototype._acceptObserver = function (o) {
  97. return o.onNext(this.value);
  98. };
  99. OnNextNotification.prototype.toString = function () {
  100. return 'OnNext(' + this.value + ')';
  101. };
  102. return OnNextNotification;
  103. }(Notification));
  104. var OnErrorNotification = (function (__super__) {
  105. inherits(OnErrorNotification, __super__);
  106. function OnErrorNotification(error) {
  107. this.error = error;
  108. this.kind = 'E';
  109. }
  110. OnErrorNotification.prototype._accept = function (onNext, onError) {
  111. return onError(this.error);
  112. };
  113. OnErrorNotification.prototype._acceptObserver = function (o) {
  114. return o.onError(this.error);
  115. };
  116. OnErrorNotification.prototype.toString = function () {
  117. return 'OnError(' + this.error + ')';
  118. };
  119. return OnErrorNotification;
  120. }(Notification));
  121. var OnCompletedNotification = (function (__super__) {
  122. inherits(OnCompletedNotification, __super__);
  123. function OnCompletedNotification() {
  124. this.kind = 'C';
  125. }
  126. OnCompletedNotification.prototype._accept = function (onNext, onError, onCompleted) {
  127. return onCompleted();
  128. };
  129. OnCompletedNotification.prototype._acceptObserver = function (o) {
  130. return o.onCompleted();
  131. };
  132. OnCompletedNotification.prototype.toString = function () {
  133. return 'OnCompleted()';
  134. };
  135. return OnCompletedNotification;
  136. }(Notification));
  137. /**
  138. * Creates an object that represents an OnNext notification to an observer.
  139. * @param {Any} value The value contained in the notification.
  140. * @returns {Notification} The OnNext notification containing the value.
  141. */
  142. var notificationCreateOnNext = Notification.createOnNext = function (value) {
  143. return new OnNextNotification(value);
  144. };
  145. /**
  146. * Creates an object that represents an OnError notification to an observer.
  147. * @param {Any} error The exception contained in the notification.
  148. * @returns {Notification} The OnError notification containing the exception.
  149. */
  150. var notificationCreateOnError = Notification.createOnError = function (error) {
  151. return new OnErrorNotification(error);
  152. };
  153. /**
  154. * Creates an object that represents an OnCompleted notification to an observer.
  155. * @returns {Notification} The OnCompleted notification.
  156. */
  157. var notificationCreateOnCompleted = Notification.createOnCompleted = function () {
  158. return new OnCompletedNotification();
  159. };
  160. /** Used to determine if values are of the language type Object */
  161. var dontEnums = ['toString',
  162. 'toLocaleString',
  163. 'valueOf',
  164. 'hasOwnProperty',
  165. 'isPrototypeOf',
  166. 'propertyIsEnumerable',
  167. 'constructor'],
  168. dontEnumsLength = dontEnums.length;
  169. var argsTag = '[object Arguments]',
  170. arrayTag = '[object Array]',
  171. boolTag = '[object Boolean]',
  172. dateTag = '[object Date]',
  173. errorTag = '[object Error]',
  174. funcTag = '[object Function]',
  175. mapTag = '[object Map]',
  176. numberTag = '[object Number]',
  177. objectTag = '[object Object]',
  178. regexpTag = '[object RegExp]',
  179. setTag = '[object Set]',
  180. stringTag = '[object String]',
  181. weakMapTag = '[object WeakMap]';
  182. var arrayBufferTag = '[object ArrayBuffer]',
  183. float32Tag = '[object Float32Array]',
  184. float64Tag = '[object Float64Array]',
  185. int8Tag = '[object Int8Array]',
  186. int16Tag = '[object Int16Array]',
  187. int32Tag = '[object Int32Array]',
  188. uint8Tag = '[object Uint8Array]',
  189. uint8ClampedTag = '[object Uint8ClampedArray]',
  190. uint16Tag = '[object Uint16Array]',
  191. uint32Tag = '[object Uint32Array]';
  192. var typedArrayTags = {};
  193. typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
  194. typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
  195. typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
  196. typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
  197. typedArrayTags[uint32Tag] = true;
  198. typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
  199. typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
  200. typedArrayTags[dateTag] = typedArrayTags[errorTag] =
  201. typedArrayTags[funcTag] = typedArrayTags[mapTag] =
  202. typedArrayTags[numberTag] = typedArrayTags[objectTag] =
  203. typedArrayTags[regexpTag] = typedArrayTags[setTag] =
  204. typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
  205. var objectProto = Object.prototype,
  206. hasOwnProperty = objectProto.hasOwnProperty,
  207. objToString = objectProto.toString,
  208. MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
  209. var keys = Object.keys || (function() {
  210. var hasOwnProperty = Object.prototype.hasOwnProperty,
  211. hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
  212. dontEnums = [
  213. 'toString',
  214. 'toLocaleString',
  215. 'valueOf',
  216. 'hasOwnProperty',
  217. 'isPrototypeOf',
  218. 'propertyIsEnumerable',
  219. 'constructor'
  220. ],
  221. dontEnumsLength = dontEnums.length;
  222. return function(obj) {
  223. if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
  224. throw new TypeError('Object.keys called on non-object');
  225. }
  226. var result = [], prop, i;
  227. for (prop in obj) {
  228. if (hasOwnProperty.call(obj, prop)) {
  229. result.push(prop);
  230. }
  231. }
  232. if (hasDontEnumBug) {
  233. for (i = 0; i < dontEnumsLength; i++) {
  234. if (hasOwnProperty.call(obj, dontEnums[i])) {
  235. result.push(dontEnums[i]);
  236. }
  237. }
  238. }
  239. return result;
  240. };
  241. }());
  242. function equalObjects(object, other, equalFunc, isLoose, stackA, stackB) {
  243. var objProps = keys(object),
  244. objLength = objProps.length,
  245. othProps = keys(other),
  246. othLength = othProps.length;
  247. if (objLength !== othLength && !isLoose) {
  248. return false;
  249. }
  250. var index = objLength, key;
  251. while (index--) {
  252. key = objProps[index];
  253. if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
  254. return false;
  255. }
  256. }
  257. var skipCtor = isLoose;
  258. while (++index < objLength) {
  259. key = objProps[index];
  260. var objValue = object[key],
  261. othValue = other[key],
  262. result;
  263. if (!(result === undefined ? equalFunc(objValue, othValue, isLoose, stackA, stackB) : result)) {
  264. return false;
  265. }
  266. skipCtor || (skipCtor = key === 'constructor');
  267. }
  268. if (!skipCtor) {
  269. var objCtor = object.constructor,
  270. othCtor = other.constructor;
  271. if (objCtor !== othCtor &&
  272. ('constructor' in object && 'constructor' in other) &&
  273. !(typeof objCtor === 'function' && objCtor instanceof objCtor &&
  274. typeof othCtor === 'function' && othCtor instanceof othCtor)) {
  275. return false;
  276. }
  277. }
  278. return true;
  279. }
  280. function equalByTag(object, other, tag) {
  281. switch (tag) {
  282. case boolTag:
  283. case dateTag:
  284. return +object === +other;
  285. case errorTag:
  286. return object.name === other.name && object.message === other.message;
  287. case numberTag:
  288. return (object !== +object) ?
  289. other !== +other :
  290. object === +other;
  291. case regexpTag:
  292. case stringTag:
  293. return object === (other + '');
  294. }
  295. return false;
  296. }
  297. var isObject = Rx.internals.isObject = function(value) {
  298. var type = typeof value;
  299. return !!value && (type === 'object' || type === 'function');
  300. };
  301. function isObjectLike(value) {
  302. return !!value && typeof value === 'object';
  303. }
  304. function isLength(value) {
  305. return typeof value === 'number' && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER;
  306. }
  307. var isHostObject = (function() {
  308. try {
  309. Object({ 'toString': 0 } + '');
  310. } catch(e) {
  311. return function() { return false; };
  312. }
  313. return function(value) {
  314. return typeof value.toString !== 'function' && typeof (value + '') === 'string';
  315. };
  316. }());
  317. function isTypedArray(value) {
  318. return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
  319. }
  320. var isArray = Array.isArray || function(value) {
  321. return isObjectLike(value) && isLength(value.length) && objToString.call(value) === arrayTag;
  322. };
  323. function arraySome (array, predicate) {
  324. var index = -1,
  325. length = array.length;
  326. while (++index < length) {
  327. if (predicate(array[index], index, array)) {
  328. return true;
  329. }
  330. }
  331. return false;
  332. }
  333. function equalArrays(array, other, equalFunc, isLoose, stackA, stackB) {
  334. var index = -1,
  335. arrLength = array.length,
  336. othLength = other.length;
  337. if (arrLength !== othLength && !(isLoose && othLength > arrLength)) {
  338. return false;
  339. }
  340. // Ignore non-index properties.
  341. while (++index < arrLength) {
  342. var arrValue = array[index],
  343. othValue = other[index],
  344. result;
  345. if (result !== undefined) {
  346. if (result) {
  347. continue;
  348. }
  349. return false;
  350. }
  351. // Recursively compare arrays (susceptible to call stack limits).
  352. if (isLoose) {
  353. if (!arraySome(other, function(othValue) {
  354. return arrValue === othValue || equalFunc(arrValue, othValue, isLoose, stackA, stackB);
  355. })) {
  356. return false;
  357. }
  358. } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, isLoose, stackA, stackB))) {
  359. return false;
  360. }
  361. }
  362. return true;
  363. }
  364. function baseIsEqualDeep(object, other, equalFunc, isLoose, stackA, stackB) {
  365. var objIsArr = isArray(object),
  366. othIsArr = isArray(other),
  367. objTag = arrayTag,
  368. othTag = arrayTag;
  369. if (!objIsArr) {
  370. objTag = objToString.call(object);
  371. if (objTag === argsTag) {
  372. objTag = objectTag;
  373. } else if (objTag !== objectTag) {
  374. objIsArr = isTypedArray(object);
  375. }
  376. }
  377. if (!othIsArr) {
  378. othTag = objToString.call(other);
  379. if (othTag === argsTag) {
  380. othTag = objectTag;
  381. }
  382. }
  383. var objIsObj = objTag === objectTag && !isHostObject(object),
  384. othIsObj = othTag === objectTag && !isHostObject(other),
  385. isSameTag = objTag === othTag;
  386. if (isSameTag && !(objIsArr || objIsObj)) {
  387. return equalByTag(object, other, objTag);
  388. }
  389. if (!isLoose) {
  390. var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
  391. othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
  392. if (objIsWrapped || othIsWrapped) {
  393. return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, isLoose, stackA, stackB);
  394. }
  395. }
  396. if (!isSameTag) {
  397. return false;
  398. }
  399. // Assume cyclic values are equal.
  400. // For more information on detecting circular references see https://es5.github.io/#JO.
  401. stackA || (stackA = []);
  402. stackB || (stackB = []);
  403. var length = stackA.length;
  404. while (length--) {
  405. if (stackA[length] === object) {
  406. return stackB[length] === other;
  407. }
  408. }
  409. // Add `object` and `other` to the stack of traversed objects.
  410. stackA.push(object);
  411. stackB.push(other);
  412. var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, isLoose, stackA, stackB);
  413. stackA.pop();
  414. stackB.pop();
  415. return result;
  416. }
  417. function baseIsEqual(value, other, isLoose, stackA, stackB) {
  418. if (value === other) {
  419. return true;
  420. }
  421. if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
  422. return value !== value && other !== other;
  423. }
  424. return baseIsEqualDeep(value, other, baseIsEqual, isLoose, stackA, stackB);
  425. }
  426. var isEqual = Rx.internals.isEqual = function (value, other) {
  427. return baseIsEqual(value, other);
  428. };
  429. var SchedulePeriodicRecursive = Rx.internals.SchedulePeriodicRecursive = (function () {
  430. function createTick(self) {
  431. return function tick(command, recurse) {
  432. recurse(0, self._period);
  433. var state = tryCatch(self._action)(self._state);
  434. if (state === errorObj) {
  435. self._cancel.dispose();
  436. thrower(state.e);
  437. }
  438. self._state = state;
  439. };
  440. }
  441. function SchedulePeriodicRecursive(scheduler, state, period, action) {
  442. this._scheduler = scheduler;
  443. this._state = state;
  444. this._period = period;
  445. this._action = action;
  446. }
  447. SchedulePeriodicRecursive.prototype.start = function () {
  448. var d = new SingleAssignmentDisposable();
  449. this._cancel = d;
  450. d.setDisposable(this._scheduler.scheduleRecursiveFuture(0, this._period, createTick(this)));
  451. return d;
  452. };
  453. return SchedulePeriodicRecursive;
  454. }());
  455. /** Provides a set of extension methods for virtual time scheduling. */
  456. var VirtualTimeScheduler = Rx.VirtualTimeScheduler = (function (__super__) {
  457. inherits(VirtualTimeScheduler, __super__);
  458. /**
  459. * Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer.
  460. *
  461. * @constructor
  462. * @param {Number} initialClock Initial value for the clock.
  463. * @param {Function} comparer Comparer to determine causality of events based on absolute time.
  464. */
  465. function VirtualTimeScheduler(initialClock, comparer) {
  466. this.clock = initialClock;
  467. this.comparer = comparer;
  468. this.isEnabled = false;
  469. this.queue = new PriorityQueue(1024);
  470. __super__.call(this);
  471. }
  472. var VirtualTimeSchedulerPrototype = VirtualTimeScheduler.prototype;
  473. VirtualTimeSchedulerPrototype.now = function () {
  474. return this.toAbsoluteTime(this.clock);
  475. };
  476. VirtualTimeSchedulerPrototype.schedule = function (state, action) {
  477. return this.scheduleAbsolute(state, this.clock, action);
  478. };
  479. VirtualTimeSchedulerPrototype.scheduleFuture = function (state, dueTime, action) {
  480. var dt = dueTime instanceof Date ?
  481. this.toRelativeTime(dueTime - this.now()) :
  482. this.toRelativeTime(dueTime);
  483. return this.scheduleRelative(state, dt, action);
  484. };
  485. /**
  486. * Adds a relative time value to an absolute time value.
  487. * @param {Number} absolute Absolute virtual time value.
  488. * @param {Number} relative Relative virtual time value to add.
  489. * @return {Number} Resulting absolute virtual time sum value.
  490. */
  491. VirtualTimeSchedulerPrototype.add = notImplemented;
  492. /**
  493. * Converts an absolute time to a number
  494. * @param {Any} The absolute time.
  495. * @returns {Number} The absolute time in ms
  496. */
  497. VirtualTimeSchedulerPrototype.toAbsoluteTime = notImplemented;
  498. /**
  499. * Converts the TimeSpan value to a relative virtual time value.
  500. * @param {Number} timeSpan TimeSpan value to convert.
  501. * @return {Number} Corresponding relative virtual time value.
  502. */
  503. VirtualTimeSchedulerPrototype.toRelativeTime = notImplemented;
  504. /**
  505. * Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities. The periodic task will be emulated using recursive scheduling.
  506. * @param {Mixed} state Initial state passed to the action upon the first iteration.
  507. * @param {Number} period Period for running the work periodically.
  508. * @param {Function} action Action to be executed, potentially updating the state.
  509. * @returns {Disposable} The disposable object used to cancel the scheduled recurring action (best effort).
  510. */
  511. VirtualTimeSchedulerPrototype.schedulePeriodic = function (state, period, action) {
  512. var s = new SchedulePeriodicRecursive(this, state, period, action);
  513. return s.start();
  514. };
  515. /**
  516. * Schedules an action to be executed after dueTime.
  517. * @param {Mixed} state State passed to the action to be executed.
  518. * @param {Number} dueTime Relative time after which to execute the action.
  519. * @param {Function} action Action to be executed.
  520. * @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
  521. */
  522. VirtualTimeSchedulerPrototype.scheduleRelative = function (state, dueTime, action) {
  523. var runAt = this.add(this.clock, dueTime);
  524. return this.scheduleAbsolute(state, runAt, action);
  525. };
  526. /**
  527. * Starts the virtual time scheduler.
  528. */
  529. VirtualTimeSchedulerPrototype.start = function () {
  530. if (!this.isEnabled) {
  531. this.isEnabled = true;
  532. do {
  533. var next = this.getNext();
  534. if (next !== null) {
  535. this.comparer(next.dueTime, this.clock) > 0 && (this.clock = next.dueTime);
  536. next.invoke();
  537. } else {
  538. this.isEnabled = false;
  539. }
  540. } while (this.isEnabled);
  541. }
  542. };
  543. /**
  544. * Stops the virtual time scheduler.
  545. */
  546. VirtualTimeSchedulerPrototype.stop = function () {
  547. this.isEnabled = false;
  548. };
  549. /**
  550. * Advances the scheduler's clock to the specified time, running all work till that point.
  551. * @param {Number} time Absolute time to advance the scheduler's clock to.
  552. */
  553. VirtualTimeSchedulerPrototype.advanceTo = function (time) {
  554. var dueToClock = this.comparer(this.clock, time);
  555. if (this.comparer(this.clock, time) > 0) { throw new ArgumentOutOfRangeError(); }
  556. if (dueToClock === 0) { return; }
  557. if (!this.isEnabled) {
  558. this.isEnabled = true;
  559. do {
  560. var next = this.getNext();
  561. if (next !== null && this.comparer(next.dueTime, time) <= 0) {
  562. this.comparer(next.dueTime, this.clock) > 0 && (this.clock = next.dueTime);
  563. next.invoke();
  564. } else {
  565. this.isEnabled = false;
  566. }
  567. } while (this.isEnabled);
  568. this.clock = time;
  569. }
  570. };
  571. /**
  572. * Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan.
  573. * @param {Number} time Relative time to advance the scheduler's clock by.
  574. */
  575. VirtualTimeSchedulerPrototype.advanceBy = function (time) {
  576. var dt = this.add(this.clock, time),
  577. dueToClock = this.comparer(this.clock, dt);
  578. if (dueToClock > 0) { throw new ArgumentOutOfRangeError(); }
  579. if (dueToClock === 0) { return; }
  580. this.advanceTo(dt);
  581. };
  582. /**
  583. * Advances the scheduler's clock by the specified relative time.
  584. * @param {Number} time Relative time to advance the scheduler's clock by.
  585. */
  586. VirtualTimeSchedulerPrototype.sleep = function (time) {
  587. var dt = this.add(this.clock, time);
  588. if (this.comparer(this.clock, dt) >= 0) { throw new ArgumentOutOfRangeError(); }
  589. this.clock = dt;
  590. };
  591. /**
  592. * Gets the next scheduled item to be executed.
  593. * @returns {ScheduledItem} The next scheduled item.
  594. */
  595. VirtualTimeSchedulerPrototype.getNext = function () {
  596. while (this.queue.length > 0) {
  597. var next = this.queue.peek();
  598. if (next.isCancelled()) {
  599. this.queue.dequeue();
  600. } else {
  601. return next;
  602. }
  603. }
  604. return null;
  605. };
  606. /**
  607. * Schedules an action to be executed at dueTime.
  608. * @param {Mixed} state State passed to the action to be executed.
  609. * @param {Number} dueTime Absolute time at which to execute the action.
  610. * @param {Function} action Action to be executed.
  611. * @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
  612. */
  613. VirtualTimeSchedulerPrototype.scheduleAbsolute = function (state, dueTime, action) {
  614. var self = this;
  615. function run(scheduler, state1) {
  616. self.queue.remove(si);
  617. return action(scheduler, state1);
  618. }
  619. var si = new ScheduledItem(this, state, run, dueTime, this.comparer);
  620. this.queue.enqueue(si);
  621. return si.disposable;
  622. };
  623. return VirtualTimeScheduler;
  624. }(Scheduler));
  625. function OnNextPredicate(predicate) {
  626. this.predicate = predicate;
  627. }
  628. OnNextPredicate.prototype.equals = function (other) {
  629. if (other === this) { return true; }
  630. if (other == null) { return false; }
  631. if (other.kind !== 'N') { return false; }
  632. return this.predicate(other.value);
  633. };
  634. function OnErrorPredicate(predicate) {
  635. this.predicate = predicate;
  636. }
  637. OnErrorPredicate.prototype.equals = function (other) {
  638. if (other === this) { return true; }
  639. if (other == null) { return false; }
  640. if (other.kind !== 'E') { return false; }
  641. return this.predicate(other.error);
  642. };
  643. var ReactiveTest = Rx.ReactiveTest = {
  644. /** Default virtual time used for creation of observable sequences in unit tests. */
  645. created: 100,
  646. /** Default virtual time used to subscribe to observable sequences in unit tests. */
  647. subscribed: 200,
  648. /** Default virtual time used to dispose subscriptions in unit tests. */
  649. disposed: 1000,
  650. /**
  651. * Factory method for an OnNext notification record at a given time with a given value or a predicate function.
  652. *
  653. * 1 - ReactiveTest.onNext(200, 42);
  654. * 2 - ReactiveTest.onNext(200, function (x) { return x.length == 2; });
  655. *
  656. * @param ticks Recorded virtual time the OnNext notification occurs.
  657. * @param value Recorded value stored in the OnNext notification or a predicate.
  658. * @return Recorded OnNext notification.
  659. */
  660. onNext: function (ticks, value) {
  661. return typeof value === 'function' ?
  662. new Recorded(ticks, new OnNextPredicate(value)) :
  663. new Recorded(ticks, Notification.createOnNext(value));
  664. },
  665. /**
  666. * Factory method for an OnError notification record at a given time with a given error.
  667. *
  668. * 1 - ReactiveTest.onNext(200, new Error('error'));
  669. * 2 - ReactiveTest.onNext(200, function (e) { return e.message === 'error'; });
  670. *
  671. * @param ticks Recorded virtual time the OnError notification occurs.
  672. * @param exception Recorded exception stored in the OnError notification.
  673. * @return Recorded OnError notification.
  674. */
  675. onError: function (ticks, error) {
  676. return typeof error === 'function' ?
  677. new Recorded(ticks, new OnErrorPredicate(error)) :
  678. new Recorded(ticks, Notification.createOnError(error));
  679. },
  680. /**
  681. * Factory method for an OnCompleted notification record at a given time.
  682. *
  683. * @param ticks Recorded virtual time the OnCompleted notification occurs.
  684. * @return Recorded OnCompleted notification.
  685. */
  686. onCompleted: function (ticks) {
  687. return new Recorded(ticks, Notification.createOnCompleted());
  688. },
  689. /**
  690. * Factory method for a subscription record based on a given subscription and disposal time.
  691. *
  692. * @param start Virtual time indicating when the subscription was created.
  693. * @param end Virtual time indicating when the subscription was disposed.
  694. * @return Subscription object.
  695. */
  696. subscribe: function (start, end) {
  697. return new Subscription(start, end);
  698. }
  699. };
  700. /**
  701. * Creates a new object recording the production of the specified value at the given virtual time.
  702. *
  703. * @constructor
  704. * @param {Number} time Virtual time the value was produced on.
  705. * @param {Mixed} value Value that was produced.
  706. * @param {Function} comparer An optional comparer.
  707. */
  708. var Recorded = Rx.Recorded = function (time, value, comparer) {
  709. this.time = time;
  710. this.value = value;
  711. this.comparer = comparer || defaultComparer;
  712. };
  713. /**
  714. * Checks whether the given recorded object is equal to the current instance.
  715. *
  716. * @param {Recorded} other Recorded object to check for equality.
  717. * @returns {Boolean} true if both objects are equal; false otherwise.
  718. */
  719. Recorded.prototype.equals = function (other) {
  720. return this.time === other.time && this.comparer(this.value, other.value);
  721. };
  722. /**
  723. * Returns a string representation of the current Recorded value.
  724. *
  725. * @returns {String} String representation of the current Recorded value.
  726. */
  727. Recorded.prototype.toString = function () {
  728. return this.value.toString() + '@' + this.time;
  729. };
  730. /**
  731. * Creates a new subscription object with the given virtual subscription and unsubscription time.
  732. *
  733. * @constructor
  734. * @param {Number} subscribe Virtual time at which the subscription occurred.
  735. * @param {Number} unsubscribe Virtual time at which the unsubscription occurred.
  736. */
  737. var Subscription = Rx.Subscription = function (start, end) {
  738. this.subscribe = start;
  739. this.unsubscribe = end || Number.MAX_VALUE;
  740. };
  741. /**
  742. * Checks whether the given subscription is equal to the current instance.
  743. * @param other Subscription object to check for equality.
  744. * @returns {Boolean} true if both objects are equal; false otherwise.
  745. */
  746. Subscription.prototype.equals = function (other) {
  747. return this.subscribe === other.subscribe && this.unsubscribe === other.unsubscribe;
  748. };
  749. /**
  750. * Returns a string representation of the current Subscription value.
  751. * @returns {String} String representation of the current Subscription value.
  752. */
  753. Subscription.prototype.toString = function () {
  754. return '(' + this.subscribe + ', ' + (this.unsubscribe === Number.MAX_VALUE ? 'Infinite' : this.unsubscribe) + ')';
  755. };
  756. var MockDisposable = Rx.MockDisposable = function (scheduler) {
  757. this.scheduler = scheduler;
  758. this.disposes = [];
  759. this.disposes.push(this.scheduler.clock);
  760. };
  761. MockDisposable.prototype.dispose = function () {
  762. this.disposes.push(this.scheduler.clock);
  763. };
  764. var MockObserver = (function (__super__) {
  765. inherits(MockObserver, __super__);
  766. function MockObserver(scheduler) {
  767. __super__.call(this);
  768. this.scheduler = scheduler;
  769. this.messages = [];
  770. }
  771. var MockObserverPrototype = MockObserver.prototype;
  772. MockObserverPrototype.onNext = function (value) {
  773. this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnNext(value)));
  774. };
  775. MockObserverPrototype.onError = function (e) {
  776. this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnError(e)));
  777. };
  778. MockObserverPrototype.onCompleted = function () {
  779. this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnCompleted()));
  780. };
  781. return MockObserver;
  782. })(Observer);
  783. function MockPromise(scheduler, messages) {
  784. var self = this;
  785. this.scheduler = scheduler;
  786. this.messages = messages;
  787. this.subscriptions = [];
  788. this.observers = [];
  789. for (var i = 0, len = this.messages.length; i < len; i++) {
  790. var message = this.messages[i],
  791. notification = message.value;
  792. (function (innerNotification) {
  793. scheduler.scheduleAbsolute(null, message.time, function () {
  794. var obs = self.observers.slice(0);
  795. for (var j = 0, jLen = obs.length; j < jLen; j++) {
  796. innerNotification.accept(obs[j]);
  797. }
  798. return disposableEmpty;
  799. });
  800. })(notification);
  801. }
  802. }
  803. MockPromise.prototype.then = function (onResolved, onRejected) {
  804. var self = this;
  805. this.subscriptions.push(new Subscription(this.scheduler.clock));
  806. var index = this.subscriptions.length - 1;
  807. var newPromise;
  808. var observer = Rx.Observer.create(
  809. function (x) {
  810. var retValue = onResolved(x);
  811. if (retValue && typeof retValue.then === 'function') {
  812. newPromise = retValue;
  813. } else {
  814. var ticks = self.scheduler.clock;
  815. newPromise = new MockPromise(self.scheduler, [Rx.ReactiveTest.onNext(ticks, undefined), Rx.ReactiveTest.onCompleted(ticks)]);
  816. }
  817. var idx = self.observers.indexOf(observer);
  818. self.observers.splice(idx, 1);
  819. self.subscriptions[index] = new Subscription(self.subscriptions[index].subscribe, self.scheduler.clock);
  820. },
  821. function (err) {
  822. onRejected(err);
  823. var idx = self.observers.indexOf(observer);
  824. self.observers.splice(idx, 1);
  825. self.subscriptions[index] = new Subscription(self.subscriptions[index].subscribe, self.scheduler.clock);
  826. }
  827. );
  828. this.observers.push(observer);
  829. return newPromise || new MockPromise(this.scheduler, this.messages);
  830. };
  831. var HotObservable = (function (__super__) {
  832. inherits(HotObservable, __super__);
  833. function HotObservable(scheduler, messages) {
  834. __super__.call(this);
  835. var message, notification, observable = this;
  836. this.scheduler = scheduler;
  837. this.messages = messages;
  838. this.subscriptions = [];
  839. this.observers = [];
  840. for (var i = 0, len = this.messages.length; i < len; i++) {
  841. message = this.messages[i];
  842. notification = message.value;
  843. (function (innerNotification) {
  844. scheduler.scheduleAbsolute(null, message.time, function () {
  845. var obs = observable.observers.slice(0);
  846. for (var j = 0, jLen = obs.length; j < jLen; j++) {
  847. innerNotification.accept(obs[j]);
  848. }
  849. return disposableEmpty;
  850. });
  851. })(notification);
  852. }
  853. }
  854. HotObservable.prototype._subscribe = function (o) {
  855. var observable = this;
  856. this.observers.push(o);
  857. this.subscriptions.push(new Subscription(this.scheduler.clock));
  858. var index = this.subscriptions.length - 1;
  859. return disposableCreate(function () {
  860. var idx = observable.observers.indexOf(o);
  861. observable.observers.splice(idx, 1);
  862. observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
  863. });
  864. };
  865. return HotObservable;
  866. })(Observable);
  867. var ColdObservable = (function (__super__) {
  868. inherits(ColdObservable, __super__);
  869. function ColdObservable(scheduler, messages) {
  870. __super__.call(this);
  871. this.scheduler = scheduler;
  872. this.messages = messages;
  873. this.subscriptions = [];
  874. }
  875. ColdObservable.prototype._subscribe = function (o) {
  876. var message, notification, observable = this;
  877. this.subscriptions.push(new Subscription(this.scheduler.clock));
  878. var index = this.subscriptions.length - 1;
  879. var d = new CompositeDisposable();
  880. for (var i = 0, len = this.messages.length; i < len; i++) {
  881. message = this.messages[i];
  882. notification = message.value;
  883. (function (innerNotification) {
  884. d.add(observable.scheduler.scheduleRelative(null, message.time, function () {
  885. innerNotification.accept(o);
  886. return disposableEmpty;
  887. }));
  888. })(notification);
  889. }
  890. return disposableCreate(function () {
  891. observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
  892. d.dispose();
  893. });
  894. };
  895. return ColdObservable;
  896. })(Observable);
  897. /** Virtual time scheduler used for testing applications and libraries built using Reactive Extensions. */
  898. Rx.TestScheduler = (function (__super__) {
  899. inherits(TestScheduler, __super__);
  900. function baseComparer(x, y) {
  901. return x > y ? 1 : (x < y ? -1 : 0);
  902. }
  903. function TestScheduler() {
  904. __super__.call(this, 0, baseComparer);
  905. }
  906. /**
  907. * Schedules an action to be executed at the specified virtual time.
  908. *
  909. * @param state State passed to the action to be executed.
  910. * @param dueTime Absolute virtual time at which to execute the action.
  911. * @param action Action to be executed.
  912. * @return Disposable object used to cancel the scheduled action (best effort).
  913. */
  914. TestScheduler.prototype.scheduleAbsolute = function (state, dueTime, action) {
  915. dueTime <= this.clock && (dueTime = this.clock + 1);
  916. return __super__.prototype.scheduleAbsolute.call(this, state, dueTime, action);
  917. };
  918. /**
  919. * Adds a relative virtual time to an absolute virtual time value.
  920. *
  921. * @param absolute Absolute virtual time value.
  922. * @param relative Relative virtual time value to add.
  923. * @return Resulting absolute virtual time sum value.
  924. */
  925. TestScheduler.prototype.add = function (absolute, relative) {
  926. return absolute + relative;
  927. };
  928. /**
  929. * Converts the absolute virtual time value to a DateTimeOffset value.
  930. *
  931. * @param absolute Absolute virtual time value to convert.
  932. * @return Corresponding DateTimeOffset value.
  933. */
  934. TestScheduler.prototype.toAbsoluteTime = function (absolute) {
  935. return new Date(absolute).getTime();
  936. };
  937. /**
  938. * Converts the TimeSpan value to a relative virtual time value.
  939. *
  940. * @param timeSpan TimeSpan value to convert.
  941. * @return Corresponding relative virtual time value.
  942. */
  943. TestScheduler.prototype.toRelativeTime = function (timeSpan) {
  944. return timeSpan;
  945. };
  946. /**
  947. * Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and dispose the subscription.
  948. *
  949. * @param create Factory method to create an observable sequence.
  950. * @param created Virtual time at which to invoke the factory to create an observable sequence.
  951. * @param subscribed Virtual time at which to subscribe to the created observable sequence.
  952. * @param disposed Virtual time at which to dispose the subscription.
  953. * @return Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
  954. */
  955. TestScheduler.prototype.startScheduler = function (createFn, settings) {
  956. settings || (settings = {});
  957. settings.created == null && (settings.created = ReactiveTest.created);
  958. settings.subscribed == null && (settings.subscribed = ReactiveTest.subscribed);
  959. settings.disposed == null && (settings.disposed = ReactiveTest.disposed);
  960. var observer = this.createObserver(), source, subscription;
  961. this.scheduleAbsolute(null, settings.created, function () {
  962. source = createFn();
  963. return disposableEmpty;
  964. });
  965. this.scheduleAbsolute(null, settings.subscribed, function () {
  966. subscription = source.subscribe(observer);
  967. return disposableEmpty;
  968. });
  969. this.scheduleAbsolute(null, settings.disposed, function () {
  970. subscription.dispose();
  971. return disposableEmpty;
  972. });
  973. this.start();
  974. return observer;
  975. };
  976. /**
  977. * Creates a hot observable using the specified timestamped notification messages either as an array or arguments.
  978. * @param messages Notifications to surface through the created sequence at their specified absolute virtual times.
  979. * @return Hot observable sequence that can be used to assert the timing of subscriptions and notifications.
  980. */
  981. TestScheduler.prototype.createHotObservable = function () {
  982. var len = arguments.length, args;
  983. if (Array.isArray(arguments[0])) {
  984. args = arguments[0];
  985. } else {
  986. args = new Array(len);
  987. for (var i = 0; i < len; i++) { args[i] = arguments[i]; }
  988. }
  989. return new HotObservable(this, args);
  990. };
  991. /**
  992. * Creates a cold observable using the specified timestamped notification messages either as an array or arguments.
  993. * @param messages Notifications to surface through the created sequence at their specified virtual time offsets from the sequence subscription time.
  994. * @return Cold observable sequence that can be used to assert the timing of subscriptions and notifications.
  995. */
  996. TestScheduler.prototype.createColdObservable = function () {
  997. var len = arguments.length, args;
  998. if (Array.isArray(arguments[0])) {
  999. args = arguments[0];
  1000. } else {
  1001. args = new Array(len);
  1002. for (var i = 0; i < len; i++) { args[i] = arguments[i]; }
  1003. }
  1004. return new ColdObservable(this, args);
  1005. };
  1006. /**
  1007. * Creates a resolved promise with the given value and ticks
  1008. * @param {Number} ticks The absolute time of the resolution.
  1009. * @param {Any} value The value to yield at the given tick.
  1010. * @returns {MockPromise} A mock Promise which fulfills with the given value.
  1011. */
  1012. TestScheduler.prototype.createResolvedPromise = function (ticks, value) {
  1013. return new MockPromise(this, [Rx.ReactiveTest.onNext(ticks, value), Rx.ReactiveTest.onCompleted(ticks)]);
  1014. };
  1015. /**
  1016. * Creates a rejected promise with the given reason and ticks
  1017. * @param {Number} ticks The absolute time of the resolution.
  1018. * @param {Any} reason The reason for rejection to yield at the given tick.
  1019. * @returns {MockPromise} A mock Promise which rejects with the given reason.
  1020. */
  1021. TestScheduler.prototype.createRejectedPromise = function (ticks, reason) {
  1022. return new MockPromise(this, [Rx.ReactiveTest.onError(ticks, reason)]);
  1023. };
  1024. /**
  1025. * Creates an observer that records received notification messages and timestamps those.
  1026. * @return Observer that can be used to assert the timing of received notifications.
  1027. */
  1028. TestScheduler.prototype.createObserver = function () {
  1029. return new MockObserver(this);
  1030. };
  1031. return TestScheduler;
  1032. })(VirtualTimeScheduler);
  1033. return Rx;
  1034. }));