faye 0.6.4 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of faye might be problematic. Click here for more details.

@@ -195,6 +195,12 @@ JS.ENV.EngineSpec = JS.Test.describe("Pub/sub engines", function() { with(this)
195
195
  subscribe("alice", "/messages/foo")
196
196
  }})
197
197
 
198
+ it("delivers multibyte messages correctly", function() { with(this) {
199
+ message.data = "Apple = "
200
+ publish(message)
201
+ expect_message("alice", [message])
202
+ }})
203
+
198
204
  it("delivers messages to the subscribed client", function() { with(this) {
199
205
  publish(message)
200
206
  expect_message("alice", [message])
@@ -0,0 +1,78 @@
1
+ JS.ENV.IntegrationSteps = JS.Test.asyncSteps({
2
+ server: function(port, callback) {
3
+ this._adapter = new Faye.NodeAdapter({mount: "/bayeux", timeout: 25})
4
+ this._adapter.listen(port)
5
+ this._port = port
6
+ setTimeout(callback, 100)
7
+ },
8
+
9
+ stop: function(callback) {
10
+ this._adapter.stop()
11
+ setTimeout(callback, 100)
12
+ },
13
+
14
+ client: function(name, channels, callback) {
15
+ this._clients = this._clients || {}
16
+ this._inboxes = this._inboxes || {}
17
+ this._clients[name] = new Faye.Client("http://0.0.0.0:" + this._port + "/bayeux")
18
+ this._inboxes[name] = {}
19
+
20
+ var n = channels.length
21
+ if (n === 0) return callback()
22
+
23
+ Faye.each(channels, function(channel) {
24
+ var subscription = this._clients[name].subscribe(channel, function(message) {
25
+ this._inboxes[name][channel] = this._inboxes[name][channel] || []
26
+ this._inboxes[name][channel].push(message)
27
+ }, this)
28
+ subscription.callback(function() {
29
+ n -= 1
30
+ if (n === 0) callback()
31
+ })
32
+ }, this)
33
+ },
34
+
35
+ publish: function(name, channel, message, callback) {
36
+ this._clients[name].publish(channel, message)
37
+ setTimeout(callback, 100)
38
+ },
39
+
40
+ check_inbox: function(name, channel, messages, callback) {
41
+ var inbox = this._inboxes[name][channel] || []
42
+ this.assertEqual(messages, inbox)
43
+ callback()
44
+ }
45
+ })
46
+
47
+ JS.ENV.Server.IntegrationSpec = JS.Test.describe("Server integration", function() { with(this) {
48
+ include(IntegrationSteps)
49
+
50
+ before(function() { with(this) {
51
+ server(8000)
52
+ client("alice", [])
53
+ client("bob", ["/foo"])
54
+ }})
55
+
56
+ after(function() { this.stop() })
57
+
58
+ it("delivers a message between clients", function() { with(this) {
59
+ publish("alice", "/foo", {hello: "world"})
60
+ check_inbox("bob", "/foo", [{hello: "world"}])
61
+ }})
62
+
63
+ it("does not deliver messages for unsubscribed channels", function() { with(this) {
64
+ publish("alice", "/bar", {hello: "world"})
65
+ check_inbox("bob", "/foo", [])
66
+ }})
67
+
68
+ it("delivers multiple messages", function() { with(this) {
69
+ publish("alice", "/foo", {hello: "world"})
70
+ publish("alice", "/foo", {hello: "world"})
71
+ check_inbox("bob", "/foo", [{hello: "world"},{hello: "world"}])
72
+ }})
73
+
74
+ it("delivers multibyte strings", function() { with(this) {
75
+ publish("alice", "/foo", {hello: "Apple = "})
76
+ check_inbox("bob", "/foo", [{hello: "Apple = "}])
77
+ }})
78
+ }})
@@ -0,0 +1,39 @@
1
+ JS.ENV.WebSocket = JS.ENV.WebSocket || {}
2
+
3
+ JS.ENV.WebSocket.Draft75ParserSpec = JS.Test.describe("WebSocket.Draft75Parser", function() { with(this) {
4
+ before(function() { with(this) {
5
+ this.webSocket = {dispatchEvent: function() {}}
6
+ this.socket = new FakeSocket
7
+ this.parser = new Faye.WebSocket.Draft75Parser(webSocket, socket)
8
+ }})
9
+
10
+ describe("parse", function() { with(this) {
11
+ it("parses text frames", function() { with(this) {
12
+ expect(webSocket, "receive").given("Hello")
13
+ parser.parse([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff])
14
+ }})
15
+
16
+ it("parses multibyte text frames", function() { with(this) {
17
+ expect(webSocket, "receive").given("Apple = ")
18
+ parser.parse([0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff])
19
+ }})
20
+
21
+ it("parses fragmented frames", function() { with(this) {
22
+ expect(webSocket, "receive").given("Hello")
23
+ parser.parse([0x00, 0x48, 0x65, 0x6c])
24
+ parser.parse([0x6c, 0x6f, 0xff])
25
+ }})
26
+ }})
27
+
28
+ describe("frame", function() { with(this) {
29
+ it("returns the given string formatted as a WebSocket frame", function() { with(this) {
30
+ parser.frame("Hello")
31
+ assertEqual( [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], socket.read() )
32
+ }})
33
+
34
+ it("encodes multibyte characters correctly", function() { with(this) {
35
+ parser.frame("Apple = ")
36
+ assertEqual( [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff], socket.read() )
37
+ }})
38
+ }})
39
+ }})
@@ -0,0 +1,130 @@
1
+ JS.ENV.WebSocket = JS.ENV.WebSocket || {}
2
+
3
+ JS.ENV.WebSocket.Protocol8ParserSpec = JS.Test.describe("WebSocket.Protocol8Parser", function() { with(this) {
4
+ before(function() { with(this) {
5
+ this.webSocket = {dispatchEvent: function() {}}
6
+ this.socket = new FakeSocket
7
+ this.parser = new Faye.WebSocket.Protocol8Parser(webSocket, socket)
8
+ }})
9
+
10
+ define("parse", function() {
11
+ var bytes = [];
12
+ for (var i = 0, n = arguments.length; i < n; i++) bytes = bytes.concat(arguments[i])
13
+ this.parser.parse(new Buffer(bytes))
14
+ })
15
+
16
+ describe("parse", function() { with(this) {
17
+ define("mask", function() {
18
+ return this._mask = this._mask || Faye.map([1,2,3,4], function() { return Math.floor(Math.random() * 255) })
19
+ })
20
+
21
+ define("maskMessage", function(bytes) {
22
+ var output = []
23
+ Array.prototype.forEach.call(bytes, function(b, i) {
24
+ output[i] = bytes[i] ^ this.mask()[i % 4]
25
+ }, this)
26
+ return output
27
+ })
28
+
29
+ it("parses unmasked text frames", function() { with(this) {
30
+ expect(webSocket, "receive").given("Hello")
31
+ parse([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f])
32
+ }})
33
+
34
+ it("parses fragmented text frames", function() { with(this) {
35
+ expect(webSocket, "receive").given("Hello")
36
+ parse([0x01, 0x03, 0x48, 0x65, 0x6c])
37
+ parse([0x80, 0x02, 0x6c, 0x6f])
38
+ }})
39
+
40
+ it("parses masked text frames", function() { with(this) {
41
+ expect(webSocket, "receive").given("Hello")
42
+ parse([0x81, 0x85], mask(), maskMessage([0x48, 0x65, 0x6c, 0x6c, 0x6f]))
43
+ }})
44
+
45
+ it("parses masked fragmented text frames", function() { with(this) {
46
+ expect(webSocket, "receive").given("Hello")
47
+ parse([0x01, 0x81], mask(), maskMessage([0x48]))
48
+ parse([0x80, 0x84], mask(), maskMessage([0x65, 0x6c, 0x6c, 0x6f]))
49
+ }})
50
+
51
+ it("closes the socket if the frame has an unrecognized opcode", function() { with(this) {
52
+ expect(webSocket, "close").given("protocol_error")
53
+ parse([0x83, 0x00])
54
+ }})
55
+
56
+ it("parses unmasked multibyte text frames", function() { with(this) {
57
+ expect(webSocket, "receive").given("Apple = ")
58
+ parse([0x81, 0x0b, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf])
59
+ }})
60
+
61
+ it("parses fragmented multibyte text frames", function() { with(this) {
62
+ expect(webSocket, "receive").given("Apple = ")
63
+ parse([0x01, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3])
64
+ parse([0x80, 0x01, 0xbf])
65
+ }})
66
+
67
+ it("parses masked multibyte text frames", function() { with(this) {
68
+ expect(webSocket, "receive").given("Apple = ")
69
+ parse([0x81, 0x8b], mask(), maskMessage([0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf]))
70
+ }})
71
+
72
+ it("parses masked fragmented multibyte text frames", function() { with(this) {
73
+ expect(webSocket, "receive").given("Apple = ")
74
+ parse([0x01, 0x8a], mask(), maskMessage([0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3]))
75
+ parse([0x80, 0x81], mask(), maskMessage([0xbf]))
76
+ }})
77
+
78
+ it("parses unmasked medium-length text frames", function() { with(this) {
79
+ expect(webSocket, "receive").given("HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello")
80
+ parse([129, 126, 0, 200, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111])
81
+ }})
82
+
83
+ it("parses masked medium-length text frames", function() { with(this) {
84
+ expect(webSocket, "receive").given("HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello")
85
+ parse([129, 254, 0, 200], mask(), maskMessage([72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111]))
86
+ }})
87
+
88
+ it("replies to pings with a pong", function() { with(this) {
89
+ expect(webSocket, "send").given("OHAI", "pong")
90
+ parse([0x89, 0x04, 0x4f, 0x48, 0x41, 0x49])
91
+ }})
92
+ }})
93
+
94
+ describe("frame", function() { with(this) {
95
+ it("returns the given string formatted as a WebSocket frame", function() { with(this) {
96
+ parser.frame("Hello")
97
+ assertEqual( [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f], socket.read() )
98
+ }})
99
+
100
+ it("encodes multibyte characters correctly", function() { with(this) {
101
+ parser.frame("Apple = ")
102
+ assertEqual( [0x81, 0x0b, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf], socket.read() )
103
+ }})
104
+
105
+ it("encodes medium-length strings using extra length bytes", function() { with(this) {
106
+ parser.frame("HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello")
107
+ assertEqual( [129, 126, 0, 200, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111], socket.read() )
108
+ }})
109
+
110
+ it("encodes long strings using extra length bytes", function() { with(this) {
111
+ var reps = 13108, message = '', output = [0x81, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04]
112
+ while (reps--) {
113
+ message += "Hello"
114
+ output = output.concat([0x48, 0x65, 0x6c, 0x6c, 0x6f])
115
+ }
116
+ parser.frame(message)
117
+ assertEqual( output, socket.read() )
118
+ }})
119
+
120
+ it("encodes close frames with an error code", function() { with(this) {
121
+ parser.frame("Hello", "close", "protocol_error")
122
+ assertEqual( [0x88, 0x07, 0x03, 0xea, 0x48, 0x65, 0x6c, 0x6c, 0x6f], socket.read() )
123
+ }})
124
+
125
+ it("encodes pong frames", function() { with(this) {
126
+ parser.frame("", "pong")
127
+ assertEqual( [0x8a, 0x00], socket.read() )
128
+ }})
129
+ }})
130
+ }})
data/spec/node.js CHANGED
@@ -6,6 +6,21 @@ JS.Packages(function() { with(this) {
6
6
  autoload(/.*Spec/, {from: 'spec/javascript'})
7
7
  }})
8
8
 
9
+ FakeSocket = function() {
10
+ this._fragments = []
11
+ }
12
+ FakeSocket.prototype.write = function(buffer, encoding) {
13
+ this._fragments.push([buffer, encoding])
14
+ }
15
+ FakeSocket.prototype.read = function() {
16
+ var output = []
17
+ this._fragments.forEach(function(buffer, i) {
18
+ for (var j = 0, n = buffer[0].length; j < n; j++)
19
+ output.push(buffer[0][j])
20
+ })
21
+ return output
22
+ }
23
+
9
24
  JS.require('Faye', 'JS.Test', 'JS.Range', function() {
10
25
  JS.Test.Unit.Assertions.include({
11
26
  assertYield: function(expected) {
@@ -27,6 +42,9 @@ JS.require('Faye', 'JS.Test', 'JS.Range', function() {
27
42
  'Server.SubscribeSpec',
28
43
  'Server.UnsubscribeSpec',
29
44
  'Server.ExtensionsSpec',
45
+ 'Server.IntegrationSpec',
46
+ 'WebSocket.Draft75ParserSpec',
47
+ 'WebSocket.Protocol8ParserSpec',
30
48
  'NodeAdapterSpec',
31
49
  'ClientSpec',
32
50
  'TransportSpec',
@@ -1,3 +1,5 @@
1
+ # encoding=utf-8
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  EngineSteps = EM::RSpec.async_steps do
@@ -94,6 +96,7 @@ describe "Pub/sub engines" do
94
96
  end
95
97
 
96
98
  shared_examples_for "faye engine" do
99
+ include EncodingHelper
97
100
  include EngineSteps
98
101
 
99
102
  let(:options) { {:timeout => 1} }
@@ -189,6 +192,12 @@ describe "Pub/sub engines" do
189
192
  publish @message
190
193
  expect_message :alice, [@message]
191
194
  end
195
+
196
+ it "delivers multibyte messages correctly" do
197
+ @message["data"] = encode "Apple = "
198
+ publish @message
199
+ expect_message :alice, [@message]
200
+ end
192
201
  end
193
202
 
194
203
  describe "with a subscriber that is removed" do
@@ -0,0 +1,88 @@
1
+ # encoding=utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ require "thin"
6
+ Thin::Logging.silent = true
7
+
8
+ IntegrationSteps = EM::RSpec.async_steps do
9
+ def server(port, &callback)
10
+ @adapter = Faye::RackAdapter.new(:mount => "/bayeux", :timeout => 25)
11
+ @adapter.listen(port)
12
+ @port = port
13
+ EM.next_tick(&callback)
14
+ end
15
+
16
+ def stop(&callback)
17
+ @adapter.stop
18
+ EM.next_tick(&callback)
19
+ end
20
+
21
+ def client(name, channels, &callback)
22
+ @clients ||= {}
23
+ @inboxes ||= {}
24
+ @clients[name] = Faye::Client.new("http://0.0.0.0:#{@port}/bayeux")
25
+ @inboxes[name] = {}
26
+
27
+ n = channels.size
28
+ return callback.call if n.zero?
29
+
30
+ channels.each do |channel|
31
+ subscription = @clients[name].subscribe(channel) do |message|
32
+ @inboxes[name][channel] ||= []
33
+ @inboxes[name][channel] << message
34
+ end
35
+ subscription.callback do
36
+ n -= 1
37
+ callback.call if n.zero?
38
+ end
39
+ end
40
+ end
41
+
42
+ def publish(name, channel, message, &callback)
43
+ @clients[name].publish(channel, message)
44
+ EM.add_timer(0.1, &callback)
45
+ end
46
+
47
+ def check_inbox(name, channel, messages, &callback)
48
+ inbox = @inboxes[name][channel] || []
49
+ inbox.should == messages
50
+ callback.call
51
+ end
52
+ end
53
+
54
+ describe "server integration" do
55
+ include IntegrationSteps
56
+ include EncodingHelper
57
+
58
+ before do
59
+ Faye.ensure_reactor_running!
60
+ server 8000
61
+ client :alice, []
62
+ client :bob, ["/foo"]
63
+ sync
64
+ end
65
+
66
+ after { stop }
67
+
68
+ it "delivers a message between clients" do
69
+ publish :alice, "/foo", {"hello" => "world"}
70
+ check_inbox :bob, "/foo", [{"hello" => "world"}]
71
+ end
72
+
73
+ it "does not deliver messages for unsubscribed channels" do
74
+ publish :alice, "/bar", {"hello" => "world"}
75
+ check_inbox :bob, "/foo", []
76
+ end
77
+
78
+ it "delivers multiple messages" do
79
+ publish :alice, "/foo", {"hello" => "world"}
80
+ publish :alice, "/foo", {"hello" => "world"}
81
+ check_inbox :bob, "/foo", [{"hello" => "world"}, {"hello" => "world"}]
82
+ end
83
+
84
+ it "delivers multibyte strings" do
85
+ publish :alice, "/foo", {"hello" => encode("Apple = ")}
86
+ check_inbox :bob, "/foo", [{"hello" => encode("Apple = ")}]
87
+ end
88
+ end
@@ -0,0 +1,41 @@
1
+ # encoding=utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Faye::WebSocket::Draft75Parser do
6
+ include EncodingHelper
7
+
8
+ before do
9
+ @web_socket = mock Faye::WebSocket
10
+ @parser = Faye::WebSocket::Draft75Parser.new(@web_socket)
11
+ end
12
+
13
+ describe :parse do
14
+ it "parses text frames" do
15
+ @web_socket.should_receive(:receive).with("Hello")
16
+ parse [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
17
+ end
18
+
19
+ it "parses multibyte text frames" do
20
+ @web_socket.should_receive(:receive).with(encode "Apple = ")
21
+ parse [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]
22
+ end
23
+
24
+ it "parses fragmented frames" do
25
+ @web_socket.should_receive(:receive).with("Hello")
26
+ parse [0x00, 0x48, 0x65, 0x6c]
27
+ parse [0x6c, 0x6f, 0xff]
28
+ end
29
+ end
30
+
31
+ describe :frame do
32
+ it "returns the given string formatted as a WebSocket frame" do
33
+ bytes(@parser.frame "Hello").should == [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
34
+ end
35
+
36
+ it "encodes multibyte characters correctly" do
37
+ message = encode "Apple = "
38
+ bytes(@parser.frame message).should == [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]
39
+ end
40
+ end
41
+ end