Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: apps/moterm/moterm_driver_unittest.cc

Issue 1128333002: Moterm part 2: Add MotermDriver, a terminal "driver". (Closed) Base URL: https://github.com/domokit/mojo.git@moterm_model
Patch Set: rebased again Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « apps/moterm/moterm_driver.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 #include "apps/moterm/moterm_driver.h"
6
7 #include <deque>
8 #include <string>
9 #include <vector>
10
11 #include "base/macros.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "mojo/public/cpp/application/application_test_base.h"
15 #include "mojo/public/cpp/bindings/callback.h"
16 #include "mojo/public/cpp/bindings/type_converter.h"
17 #include "mojo/services/files/public/interfaces/file.mojom.h"
18 #include "mojo/services/files/public/interfaces/types.mojom.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace {
22
23 // A test |MotermDriver::Client| that just records the notifications it
24 // receives. When |OnDestroyed()| gets called, it'll also quit the current
25 // message loop.
26 class TestClient : public MotermDriver::Client {
27 public:
28 struct Event {
29 enum class Type { DATA_RECEIVED, CLOSED, DESTROYED };
30
31 Event(Type type) : type(type) {}
32 Event(Type type, const std::string& data) : type(type), data(data) {}
33 ~Event() {}
34
35 Type type;
36 std::string data;
37 };
38
39 TestClient() {}
40 ~TestClient() {}
41
42 std::deque<Event>& events() { return events_; }
43
44 private:
45 // |MotermDriver::Client|:
46 void OnDataReceived(const void* bytes, size_t num_bytes) override {
47 events_.push_back(
48 Event(Event::Type::DATA_RECEIVED,
49 std::string(static_cast<const char*>(bytes), num_bytes)));
50 }
51
52 void OnClosed() override { events_.push_back(Event(Event::Type::CLOSED)); }
53
54 void OnDestroyed() override {
55 events_.push_back(Event(Event::Type::DESTROYED));
56 base::MessageLoop::current()->Quit();
57 }
58
59 std::deque<Event> events_;
60
61 DISALLOW_COPY_AND_ASSIGN(TestClient);
62 };
63
64 using MotermDriverTest = mojo::test::ApplicationTestBase;
65
66 // This spins the message loop until the callback is run. Note that we can't
67 // just wait for the response message, since we need to give a chance for the
68 // driver to handle the message.
69 void FileWriteString(mojo::files::File* file, const std::string& s) {
70 std::vector<uint8_t> bytes_to_write;
71 for (size_t i = 0; i < s.size(); i++)
72 bytes_to_write.push_back(static_cast<uint8_t>(s[i]));
73 mojo::files::Error error = mojo::files::ERROR_INTERNAL;
74 uint32_t num_bytes_written = 0;
75 file->Write(mojo::Array<uint8_t>::From(bytes_to_write), 0,
76 mojo::files::WHENCE_FROM_CURRENT,
77 [&error, &num_bytes_written](mojo::files::Error e, uint32_t n) {
78 error = e;
79 num_bytes_written = n;
80 base::MessageLoop::current()->Quit();
81 });
82 base::MessageLoop::current()->Run();
83 EXPECT_EQ(mojo::files::ERROR_OK, error);
84 EXPECT_EQ(bytes_to_write.size(), num_bytes_written);
85 }
86
87 // (See the comments above |FileWriteString()|.)
88 void FileClose(mojo::files::File* file) {
89 mojo::files::Error error = mojo::files::ERROR_INTERNAL;
90 file->Close([&error](mojo::files::Error e) {
91 error = e;
92 base::MessageLoop::current()->Quit();
93 });
94 base::MessageLoop::current()->Run();
95 EXPECT_EQ(mojo::files::ERROR_OK, error);
96 }
97
98 // (See the comments above |FileWriteString()|.)
99 std::string FileReadString(mojo::files::File* file, uint32_t max_bytes) {
100 mojo::files::Error error = mojo::files::ERROR_INTERNAL;
101 mojo::Array<uint8_t> bytes_read;
102 file->Read(
103 10, 0, mojo::files::WHENCE_FROM_CURRENT,
104 [&error, &bytes_read](mojo::files::Error e, mojo::Array<uint8_t> b) {
105 error = e;
106 bytes_read = b.Pass();
107 base::MessageLoop::current()->Quit();
108 });
109 base::MessageLoop::current()->Run();
110 EXPECT_EQ(mojo::files::ERROR_OK, error);
111 if (!bytes_read.size())
112 return std::string();
113 return std::string(reinterpret_cast<const char*>(&bytes_read[0]),
114 bytes_read.size());
115 }
116
117 TEST_F(MotermDriverTest, BasicWritesToTerminal) {
118 TestClient client;
119 mojo::files::FilePtr file;
120 base::WeakPtr<MotermDriver> driver =
121 MotermDriver::Create(&client, GetProxy(&file));
122 ASSERT_TRUE(driver);
123
124 // Write a simple string.
125 std::string hello("hello");
126 FileWriteString(file.get(), hello);
127 // The client should have gotten the data.
128 ASSERT_EQ(1u, client.events().size());
129 TestClient::Event event = client.events().front();
130 client.events().pop_front();
131 EXPECT_EQ(TestClient::Event::Type::DATA_RECEIVED, event.type);
132 EXPECT_EQ(hello, event.data);
133
134 // With the default settings, it should transform \n to \r\n.
135 FileWriteString(file.get(), "world\n");
136 // The client should have gotten the data.
137 ASSERT_EQ(1u, client.events().size());
138 event = client.events().front();
139 client.events().pop_front();
140 EXPECT_EQ(TestClient::Event::Type::DATA_RECEIVED, event.type);
141 EXPECT_EQ(std::string("world\r\n"), event.data);
142
143 FileClose(file.get());
144 // The client should have gotten the closed event.
145 ASSERT_EQ(1u, client.events().size());
146 event = client.events().front();
147 client.events().pop_front();
148 EXPECT_EQ(TestClient::Event::Type::CLOSED, event.type);
149 // The driver should still be alive.
150 EXPECT_TRUE(driver);
151
152 // Actually destroying the file (and spinning the message loop) should kill
153 // the driver.
154 file.reset();
155 base::MessageLoop::current()->Run();
156 EXPECT_FALSE(driver);
157 // The client should have gotten the destroyed event.
158 ASSERT_EQ(1u, client.events().size());
159 event = client.events().front();
160 client.events().pop_front();
161 EXPECT_EQ(TestClient::Event::Type::DESTROYED, event.type);
162 }
163
164 TEST_F(MotermDriverTest, BasicReadsFromTerminal) {
165 TestClient client;
166 mojo::files::FilePtr file;
167 base::WeakPtr<MotermDriver> driver =
168 MotermDriver::Create(&client, GetProxy(&file));
169 ASSERT_TRUE(driver);
170
171 // Kick off two reads. They should be satisfied in order. (Don't use
172 // |FileReadString()| here, since we want to queue up the reads before calling
173 // |SendData()|.)
174 mojo::files::Error error1 = mojo::files::ERROR_INTERNAL;
175 mojo::Array<uint8_t> bytes_read1;
176 file->Read(
177 4, 0, mojo::files::WHENCE_FROM_CURRENT,
178 [&error1, &bytes_read1](mojo::files::Error e, mojo::Array<uint8_t> b) {
179 error1 = e;
180 bytes_read1 = b.Pass();
181 });
182 // Only quit the message loop on getting the response to the second.
183 mojo::files::Error error2 = mojo::files::ERROR_INTERNAL;
184 mojo::Array<uint8_t> bytes_read2;
185 file->Read(
186 100, 0, mojo::files::WHENCE_FROM_CURRENT,
187 [&error2, &bytes_read2](mojo::files::Error e, mojo::Array<uint8_t> b) {
188 error2 = e;
189 bytes_read2 = b.Pass();
190 base::MessageLoop::current()->Quit();
191 });
192 // They'll only be satisfied on input from the terminal. (We need to send a
193 // \n (or a \r), since by default we start off in canonical mode.)
194 std::string data("hello\nworld");
195 driver->SendData(data.data(), data.size());
196 base::MessageLoop::current()->Run();
197 EXPECT_EQ(mojo::files::ERROR_OK, error1);
198 EXPECT_EQ(4u, bytes_read1.size());
199 EXPECT_EQ(std::string("hell"),
200 std::string(reinterpret_cast<const char*>(&bytes_read1[0]),
201 bytes_read1.size()));
202 EXPECT_EQ(mojo::files::ERROR_OK, error2);
203 EXPECT_EQ(2u, bytes_read2.size());
204 // (We shouldn't get the "world" yet.)
205 EXPECT_EQ(std::string("o\n"),
206 std::string(reinterpret_cast<const char*>(&bytes_read2[0]),
207 bytes_read2.size()));
208
209 // Now send a \n.
210 driver->SendData("\n", 1);
211 // And then do a read.
212 std::string result = FileReadString(file.get(), 100);
213 // It should now get the "world" (and the \n).
214 EXPECT_EQ(std::string("world\n"), result);
215
216 // A bunch of stuff should have been echoed to the terminal. (Don't worry
217 // about what was "displayed" here -- we'll test that separately.)
218 for (const auto& event : client.events())
219 EXPECT_EQ(TestClient::Event::Type::DATA_RECEIVED, event.type);
220 client.events().clear();
221
222 file.reset();
223 base::MessageLoop::current()->Run();
224 EXPECT_FALSE(driver);
225 // The client should have gotten the destroyed event.
226 ASSERT_EQ(1u, client.events().size());
227 TestClient::Event event = client.events().front();
228 client.events().pop_front();
229 EXPECT_EQ(TestClient::Event::Type::DESTROYED, event.type);
230 }
231
232 TEST_F(MotermDriverTest, BasicLineEditing) {
233 TestClient client;
234 mojo::files::FilePtr file;
235 base::WeakPtr<MotermDriver> driver =
236 MotermDriver::Create(&client, GetProxy(&file));
237 ASSERT_TRUE(driver);
238
239 // The default erase character is DEL (\x7f). (In case you're wondering, the
240 // string literal is split to avoid an "out of range" hex constant.)
241 std::string data(
242 "abde\x7f\x7f"
243 "cdef\n\x7f"
244 "124\x7f"
245 "345\r\nXYZ");
246 driver->SendData(data.data(), data.size());
247 std::string result = FileReadString(file.get(), 100);
248 EXPECT_EQ(std::string("abcdef\n"), result);
249 result = FileReadString(file.get(), 100);
250 // By default, ICRNL is set, so \r gets transformed to \n.
251 EXPECT_EQ(std::string("12345\n"), result);
252 result = FileReadString(file.get(), 100);
253 EXPECT_EQ(std::string("\n"), result);
254
255 // A bunch of stuff should have been echoed to the terminal. The driver has
256 // some discretion about how it splits up the stuff (into
257 // |OnDataReceived()|s), so just concatenate the contents and verify that.
258 std::string echoed;
259 for (const auto& event : client.events()) {
260 EXPECT_EQ(TestClient::Event::Type::DATA_RECEIVED, event.type);
261 EXPECT_FALSE(event.data.empty());
262 echoed += event.data;
263 }
264 client.events().clear();
265 // Note: Erases should yield BS (\x08), space, BS (unless at the beginning of
266 // a line). Note that the "XYZ" hasn't been sent to us yet, but should already
267 // be echoed.
268 EXPECT_EQ(std::string(
269 "abde\x08 \x08\x08 \x08"
270 "cdef\r\n124\x08 \x08"
271 "345\r\n\r\nXYZ"),
272 echoed);
273
274 // Here, detach rather than doing destroying the other end.
275 // TODO(vtl): Verify that the driver's end of the message pipe is closed
276 // (e.g., by checking that |file| detects that its peer is closed).
277 driver->Detach();
278 EXPECT_FALSE(driver);
279 // The client shouldn't have received anything.
280 EXPECT_TRUE(client.events().empty());
281 }
282
283 } // namespace
OLDNEW
« no previous file with comments | « apps/moterm/moterm_driver.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698