OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 // Mock out the support module to avoid depending on the message loop. |
| 6 define("mojo/public/js/support", ["timer"], function(timer) { |
| 7 var waitingCallbacks = []; |
| 8 |
| 9 function WaitCookie(id) { |
| 10 this.id = id; |
| 11 } |
| 12 |
| 13 function asyncWait(handle, flags, callback) { |
| 14 var id = waitingCallbacks.length; |
| 15 waitingCallbacks.push(callback); |
| 16 return new WaitCookie(id); |
| 17 } |
| 18 |
| 19 function cancelWait(cookie) { |
| 20 waitingCallbacks[cookie.id] = null; |
| 21 } |
| 22 |
| 23 function numberOfWaitingCallbacks() { |
| 24 var count = 0; |
| 25 for (var i = 0; i < waitingCallbacks.length; ++i) { |
| 26 if (waitingCallbacks[i]) |
| 27 ++count; |
| 28 } |
| 29 return count; |
| 30 } |
| 31 |
| 32 function pumpOnce(result) { |
| 33 var callbacks = waitingCallbacks; |
| 34 waitingCallbacks = []; |
| 35 for (var i = 0; i < callbacks.length; ++i) { |
| 36 if (callbacks[i]) |
| 37 callbacks[i](result); |
| 38 } |
| 39 } |
| 40 |
| 41 // Queue up a pumpOnce call to execute after the stack unwinds. Use |
| 42 // this to trigger a pump after all Promises are executed. |
| 43 function queuePump(result) { |
| 44 timer.createOneShot(0, pumpOnce.bind(undefined, result)); |
| 45 } |
| 46 |
| 47 var exports = {}; |
| 48 exports.asyncWait = asyncWait; |
| 49 exports.cancelWait = cancelWait; |
| 50 exports.numberOfWaitingCallbacks = numberOfWaitingCallbacks; |
| 51 exports.pumpOnce = pumpOnce; |
| 52 exports.queuePump = queuePump; |
| 53 return exports; |
| 54 }); |
| 55 |
| 56 define([ |
| 57 "gin/test/expect", |
| 58 "mojo/public/js/support", |
| 59 "mojo/public/js/core", |
| 60 "mojo/public/js/connection", |
| 61 "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom", |
| 62 "mojo/public/interfaces/bindings/tests/sample_service.mojom", |
| 63 "mojo/public/js/threading", |
| 64 "gc", |
| 65 ], function(expect, |
| 66 mockSupport, |
| 67 core, |
| 68 connection, |
| 69 sample_interfaces, |
| 70 sample_service, |
| 71 threading, |
| 72 gc) { |
| 73 testClientServer(); |
| 74 testWriteToClosedPipe(); |
| 75 testRequestResponse().then(function() { |
| 76 this.result = "PASS"; |
| 77 gc.collectGarbage(); // should not crash |
| 78 threading.quit(); |
| 79 }.bind(this)).catch(function(e) { |
| 80 this.result = "FAIL: " + (e.stack || e); |
| 81 threading.quit(); |
| 82 }.bind(this)); |
| 83 |
| 84 function testClientServer() { |
| 85 var receivedFrobinate = false; |
| 86 var receivedDidFrobinate = false; |
| 87 |
| 88 // ServiceImpl ------------------------------------------------------------ |
| 89 |
| 90 function ServiceImpl(peer) { |
| 91 this.peer = peer; |
| 92 } |
| 93 |
| 94 ServiceImpl.prototype = Object.create( |
| 95 sample_service.Service.stubClass.prototype); |
| 96 |
| 97 ServiceImpl.prototype.frobinate = function(foo, baz, port) { |
| 98 receivedFrobinate = true; |
| 99 |
| 100 expect(foo.name).toBe("Example name"); |
| 101 expect(baz).toBeTruthy(); |
| 102 expect(core.close(port)).toBe(core.RESULT_OK); |
| 103 |
| 104 this.peer.didFrobinate(42); |
| 105 }; |
| 106 |
| 107 // ServiceClientImpl ------------------------------------------------------ |
| 108 |
| 109 function ServiceClientImpl(peer) { |
| 110 this.peer = peer; |
| 111 } |
| 112 |
| 113 ServiceClientImpl.prototype = |
| 114 Object.create(sample_service.ServiceClient.stubClass.prototype); |
| 115 |
| 116 ServiceClientImpl.prototype.didFrobinate = function(result) { |
| 117 receivedDidFrobinate = true; |
| 118 |
| 119 expect(result).toBe(42); |
| 120 }; |
| 121 |
| 122 var pipe = core.createMessagePipe(); |
| 123 var anotherPipe = core.createMessagePipe(); |
| 124 var sourcePipe = core.createMessagePipe(); |
| 125 |
| 126 var connection0 = new connection.Connection( |
| 127 pipe.handle0, ServiceImpl, sample_service.ServiceClient.proxyClass); |
| 128 |
| 129 var connection1 = new connection.Connection( |
| 130 pipe.handle1, ServiceClientImpl, sample_service.Service.proxyClass); |
| 131 |
| 132 var foo = new sample_service.Foo(); |
| 133 foo.bar = new sample_service.Bar(); |
| 134 foo.name = "Example name"; |
| 135 foo.source = sourcePipe.handle0; |
| 136 connection1.remote.frobinate(foo, true, anotherPipe.handle0); |
| 137 |
| 138 mockSupport.pumpOnce(core.RESULT_OK); |
| 139 |
| 140 expect(receivedFrobinate).toBeTruthy(); |
| 141 expect(receivedDidFrobinate).toBeTruthy(); |
| 142 |
| 143 connection0.close(); |
| 144 connection1.close(); |
| 145 |
| 146 expect(mockSupport.numberOfWaitingCallbacks()).toBe(0); |
| 147 |
| 148 // sourcePipe.handle0 was closed automatically when sent over IPC. |
| 149 expect(core.close(sourcePipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); |
| 150 // sourcePipe.handle1 hasn't been closed yet. |
| 151 expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK); |
| 152 |
| 153 // anotherPipe.handle0 was closed automatically when sent over IPC. |
| 154 expect(core.close(anotherPipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); |
| 155 // anotherPipe.handle1 hasn't been closed yet. |
| 156 expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK); |
| 157 |
| 158 // The Connection object is responsible for closing these handles. |
| 159 expect(core.close(pipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); |
| 160 expect(core.close(pipe.handle1)).toBe(core.RESULT_INVALID_ARGUMENT); |
| 161 } |
| 162 |
| 163 function testWriteToClosedPipe() { |
| 164 var pipe = core.createMessagePipe(); |
| 165 |
| 166 var connection1 = new connection.Connection( |
| 167 pipe.handle1, function() {}, sample_service.Service.proxyClass); |
| 168 |
| 169 // Close the other end of the pipe. |
| 170 core.close(pipe.handle0); |
| 171 |
| 172 // Not observed yet because we haven't pumped events yet. |
| 173 expect(connection1.encounteredError()).toBeFalsy(); |
| 174 |
| 175 var foo = new sample_service.Foo(); |
| 176 foo.bar = new sample_service.Bar(); |
| 177 // TODO(darin): crbug.com/357043: pass null in place of |foo| here. |
| 178 connection1.remote.frobinate(foo, true, null); |
| 179 |
| 180 // Write failures are not reported. |
| 181 expect(connection1.encounteredError()).toBeFalsy(); |
| 182 |
| 183 // Pump events, and then we should start observing the closed pipe. |
| 184 mockSupport.pumpOnce(core.RESULT_OK); |
| 185 |
| 186 expect(connection1.encounteredError()).toBeTruthy(); |
| 187 |
| 188 connection1.close(); |
| 189 } |
| 190 |
| 191 function testRequestResponse() { |
| 192 |
| 193 // ProviderImpl ------------------------------------------------------------ |
| 194 |
| 195 function ProviderImpl(peer) { |
| 196 this.peer = peer; |
| 197 } |
| 198 |
| 199 ProviderImpl.prototype = |
| 200 Object.create(sample_interfaces.Provider.stubClass.prototype); |
| 201 |
| 202 ProviderImpl.prototype.echoString = function(a) { |
| 203 mockSupport.queuePump(core.RESULT_OK); |
| 204 return Promise.resolve({a: a}); |
| 205 }; |
| 206 |
| 207 ProviderImpl.prototype.echoStrings = function(a, b) { |
| 208 mockSupport.queuePump(core.RESULT_OK); |
| 209 return Promise.resolve({a: a, b: b}); |
| 210 }; |
| 211 |
| 212 // ProviderClientImpl ------------------------------------------------------ |
| 213 |
| 214 function ProviderClientImpl(peer) { |
| 215 this.peer = peer; |
| 216 } |
| 217 |
| 218 ProviderClientImpl.prototype = |
| 219 Object.create(sample_interfaces.ProviderClient.stubClass.prototype); |
| 220 |
| 221 var pipe = core.createMessagePipe(); |
| 222 |
| 223 var connection0 = new connection.Connection( |
| 224 pipe.handle0, |
| 225 ProviderImpl, |
| 226 sample_interfaces.ProviderClient.proxyClass); |
| 227 |
| 228 var connection1 = new connection.Connection( |
| 229 pipe.handle1, |
| 230 ProviderClientImpl, |
| 231 sample_interfaces.Provider.proxyClass); |
| 232 |
| 233 var origReadMessage = core.readMessage; |
| 234 // echoString |
| 235 mockSupport.queuePump(core.RESULT_OK); |
| 236 return connection1.remote.echoString("hello").then(function(response) { |
| 237 expect(response.a).toBe("hello"); |
| 238 }).then(function() { |
| 239 // echoStrings |
| 240 mockSupport.queuePump(core.RESULT_OK); |
| 241 return connection1.remote.echoStrings("hello", "world"); |
| 242 }).then(function(response) { |
| 243 expect(response.a).toBe("hello"); |
| 244 expect(response.b).toBe("world"); |
| 245 }).then(function() { |
| 246 // Mock a read failure, expect it to fail. |
| 247 core.readMessage = function() { |
| 248 return { result: core.RESULT_UNKNOWN }; |
| 249 }; |
| 250 mockSupport.queuePump(core.RESULT_OK); |
| 251 return connection1.remote.echoString("goodbye"); |
| 252 }).then(function() { |
| 253 throw Error("Expected echoString to fail."); |
| 254 }, function(error) { |
| 255 expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN); |
| 256 |
| 257 // Clean up. |
| 258 core.readMessage = origReadMessage; |
| 259 }); |
| 260 } |
| 261 }); |
OLD | NEW |