blob: a2ea832040faefcf7a7263de87019389334ab8e6 [file] [log] [blame]
Allen George8b96bfb2016-11-02 08:01:08 -04001// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#[macro_use]
19extern crate clap;
20extern crate ordered_float;
21extern crate thrift;
22extern crate thrift_test; // huh. I have to do this to use my lib
23
24use ordered_float::OrderedFloat;
25use std::cell::RefCell;
26use std::collections::{BTreeMap, BTreeSet};
27use std::fmt::Debug;
28use std::rc::Rc;
29
30use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TCompactInputProtocol,
31 TCompactOutputProtocol, TInputProtocol, TOutputProtocol};
32use thrift::transport::{TBufferedTransport, TFramedTransport, TTcpTransport, TTransport};
33use thrift_test::*;
34
35fn main() {
36 match run() {
37 Ok(()) => println!("cross-test client succeeded"),
38 Err(e) => {
39 println!("cross-test client failed with error {:?}", e);
40 std::process::exit(1);
41 }
42 }
43}
44
45fn run() -> thrift::Result<()> {
46 // unsupported options:
47 // --domain-socket
48 // --named-pipe
49 // --anon-pipes
50 // --ssl
51 // --threads
52 let matches = clap_app!(rust_test_client =>
53 (version: "1.0")
54 (author: "Apache Thrift Developers <dev@thrift.apache.org>")
55 (about: "Rust Thrift test client")
56 (@arg host: --host +takes_value "Host on which the Thrift test server is located")
57 (@arg port: --port +takes_value "Port on which the Thrift test server is listening")
58 (@arg transport: --transport +takes_value "Thrift transport implementation to use (\"buffered\", \"framed\")")
59 (@arg protocol: --protocol +takes_value "Thrift protocol implementation to use (\"binary\", \"compact\")")
60 (@arg testloops: -n --testloops +takes_value "Number of times to run tests")
61 ).get_matches();
62
63 let host = matches.value_of("host").unwrap_or("127.0.0.1");
64 let port = value_t!(matches, "port", u16).unwrap_or(9090);
65 let testloops = value_t!(matches, "testloops", u8).unwrap_or(1);
66 let transport = matches.value_of("transport").unwrap_or("buffered");
67 let protocol = matches.value_of("protocol").unwrap_or("binary");
68
69 let t = open_tcp_transport(host, port)?;
70
71 let t: Box<TTransport> = match transport {
72 "buffered" => Box::new(TBufferedTransport::new(t)),
73 "framed" => Box::new(TFramedTransport::new(t)),
74 unmatched => return Err(format!("unsupported transport {}", unmatched).into()),
75 };
76 let t = Rc::new(RefCell::new(t));
77
78 let (i_prot, o_prot): (Box<TInputProtocol>, Box<TOutputProtocol>) = match protocol {
79 "binary" => {
80 (Box::new(TBinaryInputProtocol::new(t.clone(), true)),
81 Box::new(TBinaryOutputProtocol::new(t.clone(), true)))
82 }
83 "compact" => {
84 (Box::new(TCompactInputProtocol::new(t.clone())),
85 Box::new(TCompactOutputProtocol::new(t.clone())))
86 }
87 unmatched => return Err(format!("unsupported protocol {}", unmatched).into()),
88 };
89
90 println!("connecting to {}:{} with {}+{} stack",
91 host,
92 port,
93 protocol,
94 transport);
95
96 let mut client = ThriftTestSyncClient::new(i_prot, o_prot);
97
98 for _ in 0..testloops {
99 make_thrift_calls(&mut client)?
100 }
101
102 Ok(())
103}
104
105// FIXME: expose "open" through the client interface so I don't have to early open the transport
106fn open_tcp_transport(host: &str, port: u16) -> thrift::Result<Rc<RefCell<Box<TTransport>>>> {
107 let mut t = TTcpTransport::new();
108 match t.open(&format!("{}:{}", host, port)) {
109 Ok(()) => Ok(Rc::new(RefCell::new(Box::new(t) as Box<TTransport>))),
110 Err(e) => Err(e),
111 }
112}
113
114fn make_thrift_calls(client: &mut ThriftTestSyncClient) -> Result<(), thrift::Error> {
115 println!("testVoid");
116 client.test_void()?;
117
118 println!("testString");
119 verify_expected_result(client.test_string("thing".to_owned()), "thing".to_owned())?;
120
121 println!("testBool");
122 verify_expected_result(client.test_bool(true), true)?;
123
124 println!("testBool");
125 verify_expected_result(client.test_bool(false), false)?;
126
127 println!("testByte");
128 verify_expected_result(client.test_byte(42), 42)?;
129
130 println!("testi32");
131 verify_expected_result(client.test_i32(1159348374), 1159348374)?;
132
133 println!("testi64");
134 // try!(verify_expected_result(client.test_i64(-8651829879438294565), -8651829879438294565));
135 verify_expected_result(client.test_i64(i64::min_value()), i64::min_value())?;
136
137 println!("testDouble");
138 verify_expected_result(client.test_double(OrderedFloat::from(42.42)),
139 OrderedFloat::from(42.42))?;
140
141 println!("testTypedef");
142 {
143 let u_snd: UserId = 2348;
144 let u_cmp: UserId = 2348;
145 verify_expected_result(client.test_typedef(u_snd), u_cmp)?;
146 }
147
148 println!("testEnum");
149 {
150 verify_expected_result(client.test_enum(Numberz::TWO), Numberz::TWO)?;
151 }
152
153 println!("testBinary");
154 {
155 let b_snd = vec![0x77, 0x30, 0x30, 0x74, 0x21, 0x20, 0x52, 0x75, 0x73, 0x74];
156 let b_cmp = vec![0x77, 0x30, 0x30, 0x74, 0x21, 0x20, 0x52, 0x75, 0x73, 0x74];
157 verify_expected_result(client.test_binary(b_snd), b_cmp)?;
158 }
159
160 println!("testStruct");
161 {
162 let x_snd = Xtruct {
163 string_thing: Some("foo".to_owned()),
164 byte_thing: Some(12),
165 i32_thing: Some(219129),
166 i64_thing: Some(12938492818),
167 };
168 let x_cmp = Xtruct {
169 string_thing: Some("foo".to_owned()),
170 byte_thing: Some(12),
171 i32_thing: Some(219129),
172 i64_thing: Some(12938492818),
173 };
174 verify_expected_result(client.test_struct(x_snd), x_cmp)?;
175 }
176
177 // Xtruct again, with optional values
178 // FIXME: apparently the erlang thrift server does not like opt-in-req-out parameters that are undefined. Joy.
179 // {
180 // let x_snd = Xtruct { string_thing: Some("foo".to_owned()), byte_thing: None, i32_thing: None, i64_thing: Some(12938492818) };
181 // let x_cmp = Xtruct { string_thing: Some("foo".to_owned()), byte_thing: Some(0), i32_thing: Some(0), i64_thing: Some(12938492818) }; // the C++ server is responding correctly
182 // try!(verify_expected_result(client.test_struct(x_snd), x_cmp));
183 // }
184 //
185
186
187 println!("testNest"); // (FIXME: try Xtruct2 with optional values)
188 {
189 let x_snd = Xtruct2 {
190 byte_thing: Some(32),
191 struct_thing: Some(Xtruct {
192 string_thing: Some("foo".to_owned()),
193 byte_thing: Some(1),
194 i32_thing: Some(324382098),
195 i64_thing: Some(12938492818),
196 }),
197 i32_thing: Some(293481098),
198 };
199 let x_cmp = Xtruct2 {
200 byte_thing: Some(32),
201 struct_thing: Some(Xtruct {
202 string_thing: Some("foo".to_owned()),
203 byte_thing: Some(1),
204 i32_thing: Some(324382098),
205 i64_thing: Some(12938492818),
206 }),
207 i32_thing: Some(293481098),
208 };
209 verify_expected_result(client.test_nest(x_snd), x_cmp)?;
210 }
211
212 println!("testList");
213 {
214 let mut v_snd: Vec<i32> = Vec::new();
215 v_snd.push(29384);
216 v_snd.push(238);
217 v_snd.push(32498);
218
219 let mut v_cmp: Vec<i32> = Vec::new();
220 v_cmp.push(29384);
221 v_cmp.push(238);
222 v_cmp.push(32498);
223
224 verify_expected_result(client.test_list(v_snd), v_cmp)?;
225 }
226
227 println!("testSet");
228 {
229 let mut s_snd: BTreeSet<i32> = BTreeSet::new();
230 s_snd.insert(293481);
231 s_snd.insert(23);
232 s_snd.insert(3234);
233
234 let mut s_cmp: BTreeSet<i32> = BTreeSet::new();
235 s_cmp.insert(293481);
236 s_cmp.insert(23);
237 s_cmp.insert(3234);
238
239 verify_expected_result(client.test_set(s_snd), s_cmp)?;
240 }
241
242 println!("testMap");
243 {
244 let mut m_snd: BTreeMap<i32, i32> = BTreeMap::new();
245 m_snd.insert(2, 4);
246 m_snd.insert(4, 6);
247 m_snd.insert(8, 7);
248
249 let mut m_cmp: BTreeMap<i32, i32> = BTreeMap::new();
250 m_cmp.insert(2, 4);
251 m_cmp.insert(4, 6);
252 m_cmp.insert(8, 7);
253
254 verify_expected_result(client.test_map(m_snd), m_cmp)?;
255 }
256
257 println!("testStringMap");
258 {
259 let mut m_snd: BTreeMap<String, String> = BTreeMap::new();
260 m_snd.insert("2".to_owned(), "4_string".to_owned());
261 m_snd.insert("4".to_owned(), "6_string".to_owned());
262 m_snd.insert("8".to_owned(), "7_string".to_owned());
263
264 let mut m_rcv: BTreeMap<String, String> = BTreeMap::new();
265 m_rcv.insert("2".to_owned(), "4_string".to_owned());
266 m_rcv.insert("4".to_owned(), "6_string".to_owned());
267 m_rcv.insert("8".to_owned(), "7_string".to_owned());
268
269 verify_expected_result(client.test_string_map(m_snd), m_rcv)?;
270 }
271
272 // nested map
273 // expect : {-4 => {-4 => -4, -3 => -3, -2 => -2, -1 => -1, }, 4 => {1 => 1, 2 => 2, 3 => 3, 4 => 4, }, }
274 println!("testMapMap");
275 {
276 let mut m_cmp_nested_0: BTreeMap<i32, i32> = BTreeMap::new();
277 for i in (-4 as i32)..0 {
278 m_cmp_nested_0.insert(i, i);
279 }
280 let mut m_cmp_nested_1: BTreeMap<i32, i32> = BTreeMap::new();
281 for i in 1..5 {
282 m_cmp_nested_1.insert(i, i);
283 }
284
285 let mut m_cmp: BTreeMap<i32, BTreeMap<i32, i32>> = BTreeMap::new();
286 m_cmp.insert(-4, m_cmp_nested_0);
287 m_cmp.insert(4, m_cmp_nested_1);
288
289 verify_expected_result(client.test_map_map(42), m_cmp)?;
290 }
291
292 println!("testMulti");
293 {
294 let mut m_snd: BTreeMap<i16, String> = BTreeMap::new();
295 m_snd.insert(1298, "fizz".to_owned());
296 m_snd.insert(-148, "buzz".to_owned());
297
298 let s_cmp = Xtruct {
299 string_thing: Some("Hello2".to_owned()),
300 byte_thing: Some(1),
301 i32_thing: Some(-123948),
302 i64_thing: Some(-19234123981),
303 };
304
305 verify_expected_result(client.test_multi(1,
306 -123948,
307 -19234123981,
308 m_snd,
309 Numberz::EIGHT,
310 81),
311 s_cmp)?;
312 }
313
314 // Insanity
315 // returns:
316 // { 1 => { 2 => argument,
317 // 3 => argument,
318 // },
319 // 2 => { 6 => <empty Insanity struct>, },
320 // }
321 {
322 let mut arg_map_usermap: BTreeMap<Numberz, i64> = BTreeMap::new();
323 arg_map_usermap.insert(Numberz::ONE, 4289);
324 arg_map_usermap.insert(Numberz::EIGHT, 19);
325
326 let mut arg_vec_xtructs: Vec<Xtruct> = Vec::new();
327 arg_vec_xtructs.push(Xtruct {
328 string_thing: Some("foo".to_owned()),
329 byte_thing: Some(8),
330 i32_thing: Some(29),
331 i64_thing: Some(92384),
332 });
333 arg_vec_xtructs.push(Xtruct {
334 string_thing: Some("bar".to_owned()),
335 byte_thing: Some(28),
336 i32_thing: Some(2),
337 i64_thing: Some(-1281),
338 });
339 arg_vec_xtructs.push(Xtruct {
340 string_thing: Some("baz".to_owned()),
341 byte_thing: Some(0),
342 i32_thing: Some(3948539),
343 i64_thing: Some(-12938492),
344 });
345
346 let mut s_cmp_nested_1: BTreeMap<Numberz, Insanity> = BTreeMap::new();
347 let insanity = Insanity {
348 user_map: Some(arg_map_usermap),
349 xtructs: Some(arg_vec_xtructs),
350 };
351 s_cmp_nested_1.insert(Numberz::TWO, insanity.clone());
352 s_cmp_nested_1.insert(Numberz::THREE, insanity.clone());
353
354 let mut s_cmp_nested_2: BTreeMap<Numberz, Insanity> = BTreeMap::new();
355 let empty_insanity = Insanity {
356 user_map: Some(BTreeMap::new()),
357 xtructs: Some(Vec::new()),
358 };
359 s_cmp_nested_2.insert(Numberz::SIX, empty_insanity);
360
361 let mut s_cmp: BTreeMap<UserId, BTreeMap<Numberz, Insanity>> = BTreeMap::new();
362 s_cmp.insert(1 as UserId, s_cmp_nested_1);
363 s_cmp.insert(2 as UserId, s_cmp_nested_2);
364
365 verify_expected_result(client.test_insanity(insanity.clone()), s_cmp)?;
366 }
367
368 println!("testException - remote throws Xception");
369 {
370 let r = client.test_exception("Xception".to_owned());
371 let x = match r {
372 Err(thrift::Error::User(ref e)) => {
373 match e.downcast_ref::<Xception>() {
374 Some(x) => Ok(x),
375 None => Err(thrift::Error::User("did not get expected Xception struct".into())),
376 }
377 }
378 _ => Err(thrift::Error::User("did not get exception".into())),
379 }?;
380
381 let x_cmp = Xception {
382 error_code: Some(1001),
383 message: Some("Xception".to_owned()),
384 };
385
386 verify_expected_result(Ok(x), &x_cmp)?;
387 }
388
389 println!("testException - remote throws TApplicationException");
390 {
391 let r = client.test_exception("TException".to_owned());
392 match r {
393 Err(thrift::Error::Application(ref e)) => {
394 println!("received an {:?}", e);
395 Ok(())
396 }
397 _ => Err(thrift::Error::User("did not get exception".into())),
398 }?;
399 }
400
401 println!("testException - remote succeeds");
402 {
403 let r = client.test_exception("foo".to_owned());
404 match r {
405 Ok(_) => Ok(()),
406 _ => Err(thrift::Error::User("received an exception".into())),
407 }?;
408 }
409
410 println!("testMultiException - remote throws Xception");
411 {
412 let r = client.test_multi_exception("Xception".to_owned(), "ignored".to_owned());
413 let x = match r {
414 Err(thrift::Error::User(ref e)) => {
415 match e.downcast_ref::<Xception>() {
416 Some(x) => Ok(x),
417 None => Err(thrift::Error::User("did not get expected Xception struct".into())),
418 }
419 }
420 _ => Err(thrift::Error::User("did not get exception".into())),
421 }?;
422
423 let x_cmp = Xception {
424 error_code: Some(1001),
425 message: Some("This is an Xception".to_owned()),
426 };
427
428 verify_expected_result(Ok(x), &x_cmp)?;
429 }
430
431 println!("testMultiException - remote throws Xception2");
432 {
433 let r = client.test_multi_exception("Xception2".to_owned(), "ignored".to_owned());
434 let x = match r {
435 Err(thrift::Error::User(ref e)) => {
436 match e.downcast_ref::<Xception2>() {
437 Some(x) => Ok(x),
438 None => Err(thrift::Error::User("did not get expected Xception struct".into())),
439 }
440 }
441 _ => Err(thrift::Error::User("did not get exception".into())),
442 }?;
443
444 let x_cmp = Xception2 {
445 error_code: Some(2002),
446 struct_thing: Some(Xtruct {
447 string_thing: Some("This is an Xception2".to_owned()),
448 byte_thing: Some(0), /* since this is an OPT_IN_REQ_OUT field the sender sets a default */
449 i32_thing: Some(0), /* since this is an OPT_IN_REQ_OUT field the sender sets a default */
450 i64_thing: Some(0), /* since this is an OPT_IN_REQ_OUT field the sender sets a default */
451 }),
452 };
453
454 verify_expected_result(Ok(x), &x_cmp)?;
455 }
456
457 println!("testMultiException - remote succeeds");
458 {
459 let r = client.test_multi_exception("haha".to_owned(), "RETURNED".to_owned());
460 let x = match r {
461 Err(e) => {
462 Err(thrift::Error::User(format!("received an unexpected exception {:?}", e).into()))
463 }
464 _ => r,
465 }?;
466
467 let x_cmp = Xtruct {
468 string_thing: Some("RETURNED".to_owned()),
469 byte_thing: Some(0), // since this is an OPT_IN_REQ_OUT field the sender sets a default
470 i32_thing: Some(0), // since this is an OPT_IN_REQ_OUT field the sender sets a default
471 i64_thing: Some(0), // since this is an OPT_IN_REQ_OUT field the sender sets a default
472 };
473
474 verify_expected_result(Ok(x), x_cmp)?;
475 }
476
477 println!("testOneWay - remote sleeps for 1 second");
478 {
479 client.test_oneway(1)?;
480 }
481
482 // final test to verify that the connection is still writable after the one-way call
483 client.test_void()
484}
485
486fn verify_expected_result<T: Debug + PartialEq + Sized>(actual: Result<T, thrift::Error>,
487 expected: T)
488 -> Result<(), thrift::Error> {
489 match actual {
490 Ok(v) => {
491 if v == expected {
492 Ok(())
493 } else {
494 Err(thrift::Error::User(format!("expected {:?} but got {:?}", &expected, &v)
495 .into()))
496 }
497 }
498 Err(e) => Err(e),
499 }
500}