test-system-input-output.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. const { id: addonID, name: addonName } = require("sdk/self");
  6. const { Cc, Ci, Cu } = require("chrome");
  7. const { Loader, LoaderWithHookedConsole2 } = require("sdk/test/loader");
  8. const { InputPort } = require("sdk/input/system");
  9. const { OutputPort } = require("sdk/output/system");
  10. const { removeObserver, addObserver,
  11. notifyObservers } = Cc["@mozilla.org/observer-service;1"].
  12. getService(Ci.nsIObserverService);
  13. const { lift, start, stop, send } = require("sdk/event/utils");
  14. const isConsoleEvent = topic =>
  15. ["console-api-log-event",
  16. "console-storage-cache-event"].indexOf(topic) >= 0;
  17. const message = x => ({wrappedJSObject: {data: x}});
  18. exports["test start / stop ports"] = assert => {
  19. const input = new InputPort({ id: Date.now().toString(32), initial: {data:0} });
  20. const topic = input.topic;
  21. assert.ok(topic.contains(addonID), "topics are namespaced to add-on");
  22. const xs = lift(({data}) => "x:" + data, input);
  23. const ys = lift(({data}) => "y:" + data, input);
  24. assert.deepEqual(input.value, {data:0}, "initila value is set");
  25. assert.deepEqual(xs.value, "x:0", "initial value is mapped");
  26. assert.deepEqual(ys.value, "y:0", "initial value is mapped");
  27. notifyObservers(message(1), topic, null);
  28. assert.deepEqual(input.value, {data:0}, "no message received on input port");
  29. assert.deepEqual(xs.value, "x:0", "no message received on xs");
  30. assert.deepEqual(ys.value, "y:0", "no message received on ys");
  31. start(xs);
  32. notifyObservers(message(2), topic, null);
  33. assert.deepEqual(input.value, {data:2}, "message received on input port");
  34. assert.deepEqual(xs.value, "x:2", "message received on xs");
  35. assert.deepEqual(ys.value, "y:2", "no message received on (not started) ys");
  36. notifyObservers(message(3), topic, null);
  37. assert.deepEqual(input.value, {data:3}, "message received on input port");
  38. assert.deepEqual(xs.value, "x:3", "message received on xs");
  39. assert.deepEqual(ys.value, "y:3", "message received on ys");
  40. notifyObservers(message(4), topic, null);
  41. assert.deepEqual(input.value, {data:4}, "message received on input port");
  42. assert.deepEqual(xs.value, "x:4", "message not received on (stopped) xs");
  43. assert.deepEqual(ys.value, "y:4", "message received on ys");
  44. stop(input);
  45. notifyObservers(message(5), topic, null);
  46. assert.deepEqual(input.value, {data:4}, "message note received on input port");
  47. assert.deepEqual(xs.value, "x:4", "message not received on (stopped) xs");
  48. assert.deepEqual(ys.value, "y:4", "message not received on (stopped) ys");
  49. };
  50. exports["test send messages to nsIObserverService"] = assert => {
  51. let messages = [];
  52. const { newURI } = Cc['@mozilla.org/network/io-service;1'].
  53. getService(Ci.nsIIOService);
  54. const output = new OutputPort({ id: Date.now().toString(32), sync: true });
  55. const topic = output.topic;
  56. const observer = {
  57. QueryInterface: function() {
  58. return this;
  59. },
  60. observe: (subject, topic, data) => {
  61. // Ignores internal console events
  62. if (!isConsoleEvent(topic)) {
  63. messages.push({
  64. topic: topic,
  65. subject: subject
  66. });
  67. }
  68. }
  69. };
  70. addObserver(observer, topic, false);
  71. send(output, null);
  72. assert.deepEqual(messages.shift(), { topic: topic, subject: null },
  73. "null message received");
  74. const uri = newURI("http://www.foo.com", null, null);
  75. send(output, uri);
  76. assert.deepEqual(messages.shift(), { topic: topic, subject: uri },
  77. "message received");
  78. function customSubject() {}
  79. send(output, customSubject);
  80. let message = messages.shift();
  81. assert.equal(message.topic, topic, "topic was received");
  82. assert.equal(message.subject.wrappedJSObject, customSubject,
  83. "custom subject is received");
  84. removeObserver(observer, topic);
  85. send(output, { data: "more data" });
  86. assert.deepEqual(messages, [],
  87. "no more data received");
  88. addObserver(observer, "*", false);
  89. send(output, { data: "data again" });
  90. message = messages.shift();
  91. assert.equal(message.topic, topic, "topic was received");
  92. assert.deepEqual(message.subject.wrappedJSObject,
  93. { data: "data again" },
  94. "wrapped message received");
  95. removeObserver(observer, "*");
  96. send(output, { data: "last data" });
  97. assert.deepEqual(messages, [],
  98. "no more data received");
  99. assert.throws(() => send(output, "hi"),
  100. /Unsupproted message type: `string`/,
  101. "strings can't be send");
  102. assert.throws(() => send(output, 4),
  103. /Unsupproted message type: `number`/,
  104. "numbers can't be send");
  105. assert.throws(() => send(output, void(0)),
  106. /Unsupproted message type: `undefined`/,
  107. "undefineds can't be send");
  108. assert.throws(() => send(output, true),
  109. /Unsupproted message type: `boolean`/,
  110. "booleans can't be send");
  111. };
  112. exports["test async OutputPort"] = (assert, done) => {
  113. let async = false;
  114. const output = new OutputPort({ id: Date.now().toString(32) });
  115. const observer = {
  116. observe: (subject, topic, data) => {
  117. removeObserver(observer, topic);
  118. assert.equal(topic, output.topic, "correct topic");
  119. assert.deepEqual(subject.wrappedJSObject, {foo: "bar"}, "message received");
  120. assert.ok(async, "message received async");
  121. done();
  122. }
  123. };
  124. addObserver(observer, output.topic, false);
  125. send(output, {foo: "bar"});
  126. assert.throws(() => send(output, "boom"), "can only send object");
  127. async = true;
  128. };
  129. exports["test explicit output topic"] = (assert, done) => {
  130. const topic = Date.now().toString(32);
  131. const output = new OutputPort({ topic: topic });
  132. const observer = {
  133. observe: (subject, topic, data) => {
  134. removeObserver(observer, topic);
  135. assert.deepEqual(subject.wrappedJSObject, {foo: "bar"}, "message received");
  136. done();
  137. }
  138. };
  139. assert.equal(output.topic, topic, "given topic is used");
  140. addObserver(observer, topic, false);
  141. send(output, {foo: "bar"});
  142. };
  143. exports["test explicit input topic"] = (assert) => {
  144. const topic = Date.now().toString(32);
  145. const input = new InputPort({ topic: topic });
  146. start(input);
  147. assert.equal(input.topic, topic, "given topic is used");
  148. notifyObservers({wrappedJSObject: {foo: "bar"}}, topic, null);
  149. assert.deepEqual(input.value, {foo: "bar"}, "message received");
  150. };
  151. exports["test receive what was send"] = assert => {
  152. const id = Date.now().toString(32);
  153. const input = new InputPort({ id: id, initial: 0});
  154. const output = new OutputPort({ id: id, sync: true });
  155. assert.ok(input.topic.contains(addonID),
  156. "input topic is namespaced to addon");
  157. assert.equal(input.topic, output.topic,
  158. "input & output get same topics from id.");
  159. start(input);
  160. assert.equal(input.value, 0, "initial value is set");
  161. send(output, { data: 1 });
  162. assert.deepEqual(input.value, {data: 1}, "message unwrapped");
  163. send(output, []);
  164. assert.deepEqual(input.value, [], "array message unwrapped");
  165. send(output, null);
  166. assert.deepEqual(input.value, null, "null message received");
  167. send(output, new String("message"));
  168. assert.deepEqual(input.value, new String("message"),
  169. "string instance received");
  170. send(output, /pattern/);
  171. assert.deepEqual(input.value, /pattern/, "regexp received");
  172. assert.throws(() => send(output, "hi"),
  173. /Unsupproted message type: `string`/,
  174. "strings can't be send");
  175. assert.throws(() => send(output, 4),
  176. /Unsupproted message type: `number`/,
  177. "numbers can't be send");
  178. assert.throws(() => send(output, void(0)),
  179. /Unsupproted message type: `undefined`/,
  180. "undefineds can't be send");
  181. assert.throws(() => send(output, true),
  182. /Unsupproted message type: `boolean`/,
  183. "booleans can't be send");
  184. stop(input);
  185. };
  186. exports["-test error reporting"] = function(assert) {
  187. let { loader, messages } = LoaderWithHookedConsole2(module);
  188. const { start, stop, lift } = loader.require("sdk/event/utils");
  189. const { InputPort } = loader.require("sdk/input/system");
  190. const { OutputPort } = loader.require("sdk/output/system");
  191. const id = "error:" + Date.now().toString(32);
  192. const raise = x => { if (x) throw new Error("foo"); };
  193. const input = new InputPort({ id: id });
  194. const output = new OutputPort({ id: id, sync: true });
  195. const xs = lift(raise, input);
  196. assert.equal(input.value, null, "initial inherited");
  197. send(output, { data: "yo yo" });
  198. assert.deepEqual(messages, [], "nothing happend yet");
  199. start(xs);
  200. send(output, { data: "first" });
  201. assert.equal(messages.length, 4, "Got an exception");
  202. assert.equal(messages[0], "console.error: " + addonName + ": \n",
  203. "error is logged");
  204. assert.ok(/Unhandled error/.test(messages[1]),
  205. "expected error message");
  206. loader.unload();
  207. };
  208. exports["test unload ends input port"] = assert => {
  209. const loader = Loader(module);
  210. const { start, stop, lift } = loader.require("sdk/event/utils");
  211. const { InputPort } = loader.require("sdk/input/system");
  212. const id = "unload!" + Date.now().toString(32);
  213. const input = new InputPort({ id: id });
  214. start(input);
  215. notifyObservers(message(1), input.topic, null);
  216. assert.deepEqual(input.value, {data: 1}, "message received");
  217. notifyObservers(message(2), input.topic, null);
  218. assert.deepEqual(input.value, {data: 2}, "message received");
  219. loader.unload();
  220. notifyObservers(message(3), input.topic, null);
  221. assert.deepEqual(input.value, {data: 2}, "message wasn't received");
  222. };
  223. require("sdk/test").run(exports);