blob: aad78a05862d909ba8762fa04ecd865d38d65078 [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;
Allen George8b96bfb2016-11-02 08:01:08 -040025use std::collections::{BTreeMap, BTreeSet};
26use std::fmt::Debug;
Allen George8b96bfb2016-11-02 08:01:08 -040027
28use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TCompactInputProtocol,
29 TCompactOutputProtocol, TInputProtocol, TOutputProtocol};
Allen George0e22c362017-01-30 07:15:00 -050030use thrift::transport::{ReadHalf, TBufferedReadTransport, TBufferedWriteTransport,
31 TFramedReadTransport, TFramedWriteTransport, TIoChannel, TReadTransport,
32 TTcpChannel, TWriteTransport, WriteHalf};
Allen George8b96bfb2016-11-02 08:01:08 -040033use 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")
Allen George0e22c362017-01-30 07:15:00 -050061 )
62 .get_matches();
Allen George8b96bfb2016-11-02 08:01:08 -040063
64 let host = matches.value_of("host").unwrap_or("127.0.0.1");
65 let port = value_t!(matches, "port", u16).unwrap_or(9090);
66 let testloops = value_t!(matches, "testloops", u8).unwrap_or(1);
67 let transport = matches.value_of("transport").unwrap_or("buffered");
68 let protocol = matches.value_of("protocol").unwrap_or("binary");
69
Allen George0e22c362017-01-30 07:15:00 -050070 let (i_chan, o_chan) = tcp_channel(host, port)?;
Allen George8b96bfb2016-11-02 08:01:08 -040071
Allen George0e22c362017-01-30 07:15:00 -050072 let (i_tran, o_tran) = match transport {
73 "buffered" => {
74 (Box::new(TBufferedReadTransport::new(i_chan)) as Box<TReadTransport>,
75 Box::new(TBufferedWriteTransport::new(o_chan)) as Box<TWriteTransport>)
76 }
77 "framed" => {
78 (Box::new(TFramedReadTransport::new(i_chan)) as Box<TReadTransport>,
79 Box::new(TFramedWriteTransport::new(o_chan)) as Box<TWriteTransport>)
80 }
Allen George8b96bfb2016-11-02 08:01:08 -040081 unmatched => return Err(format!("unsupported transport {}", unmatched).into()),
82 };
Allen George8b96bfb2016-11-02 08:01:08 -040083
84 let (i_prot, o_prot): (Box<TInputProtocol>, Box<TOutputProtocol>) = match protocol {
85 "binary" => {
Allen George0e22c362017-01-30 07:15:00 -050086 (Box::new(TBinaryInputProtocol::new(i_tran, true)),
87 Box::new(TBinaryOutputProtocol::new(o_tran, true)))
Allen George8b96bfb2016-11-02 08:01:08 -040088 }
89 "compact" => {
Allen George0e22c362017-01-30 07:15:00 -050090 (Box::new(TCompactInputProtocol::new(i_tran)),
91 Box::new(TCompactOutputProtocol::new(o_tran)))
Allen George8b96bfb2016-11-02 08:01:08 -040092 }
93 unmatched => return Err(format!("unsupported protocol {}", unmatched).into()),
94 };
95
Allen George0e22c362017-01-30 07:15:00 -050096 println!(
97 "connecting to {}:{} with {}+{} stack",
98 host,
99 port,
100 protocol,
101 transport
102 );
Allen George8b96bfb2016-11-02 08:01:08 -0400103
104 let mut client = ThriftTestSyncClient::new(i_prot, o_prot);
105
106 for _ in 0..testloops {
107 make_thrift_calls(&mut client)?
108 }
109
110 Ok(())
111}
112
Allen George0e22c362017-01-30 07:15:00 -0500113// FIXME: expose "open" through the client interface so I don't have to early
114// open
115fn tcp_channel(
116 host: &str,
117 port: u16,
118) -> thrift::Result<(ReadHalf<TTcpChannel>, WriteHalf<TTcpChannel>)> {
119 let mut c = TTcpChannel::new();
120 c.open(&format!("{}:{}", host, port))?;
121 c.split()
Allen George8b96bfb2016-11-02 08:01:08 -0400122}
123
Allen George0e22c362017-01-30 07:15:00 -0500124fn make_thrift_calls(client: &mut ThriftTestSyncClient<Box<TInputProtocol>, Box<TOutputProtocol>>,)
125 -> Result<(), thrift::Error> {
Allen George8b96bfb2016-11-02 08:01:08 -0400126 println!("testVoid");
127 client.test_void()?;
128
129 println!("testString");
130 verify_expected_result(client.test_string("thing".to_owned()), "thing".to_owned())?;
131
132 println!("testBool");
133 verify_expected_result(client.test_bool(true), true)?;
134
135 println!("testBool");
136 verify_expected_result(client.test_bool(false), false)?;
137
138 println!("testByte");
139 verify_expected_result(client.test_byte(42), 42)?;
140
141 println!("testi32");
142 verify_expected_result(client.test_i32(1159348374), 1159348374)?;
143
144 println!("testi64");
Allen George0e22c362017-01-30 07:15:00 -0500145 // try!(verify_expected_result(client.test_i64(-8651829879438294565),
146 // -8651829879438294565));
Allen George8b96bfb2016-11-02 08:01:08 -0400147 verify_expected_result(client.test_i64(i64::min_value()), i64::min_value())?;
148
149 println!("testDouble");
Allen George0e22c362017-01-30 07:15:00 -0500150 verify_expected_result(
151 client.test_double(OrderedFloat::from(42.42)),
152 OrderedFloat::from(42.42),
153 )?;
Allen George8b96bfb2016-11-02 08:01:08 -0400154
155 println!("testTypedef");
156 {
157 let u_snd: UserId = 2348;
158 let u_cmp: UserId = 2348;
159 verify_expected_result(client.test_typedef(u_snd), u_cmp)?;
160 }
161
162 println!("testEnum");
163 {
164 verify_expected_result(client.test_enum(Numberz::TWO), Numberz::TWO)?;
165 }
166
167 println!("testBinary");
168 {
169 let b_snd = vec![0x77, 0x30, 0x30, 0x74, 0x21, 0x20, 0x52, 0x75, 0x73, 0x74];
170 let b_cmp = vec![0x77, 0x30, 0x30, 0x74, 0x21, 0x20, 0x52, 0x75, 0x73, 0x74];
171 verify_expected_result(client.test_binary(b_snd), b_cmp)?;
172 }
173
174 println!("testStruct");
175 {
176 let x_snd = Xtruct {
177 string_thing: Some("foo".to_owned()),
178 byte_thing: Some(12),
179 i32_thing: Some(219129),
180 i64_thing: Some(12938492818),
181 };
182 let x_cmp = Xtruct {
183 string_thing: Some("foo".to_owned()),
184 byte_thing: Some(12),
185 i32_thing: Some(219129),
186 i64_thing: Some(12938492818),
187 };
188 verify_expected_result(client.test_struct(x_snd), x_cmp)?;
189 }
190
191 // Xtruct again, with optional values
Allen George0e22c362017-01-30 07:15:00 -0500192 // FIXME: apparently the erlang thrift server does not like opt-in-req-out
193 // parameters that are undefined. Joy.
Allen George8b96bfb2016-11-02 08:01:08 -0400194 // {
Allen George0e22c362017-01-30 07:15:00 -0500195 // let x_snd = Xtruct { string_thing: Some("foo".to_owned()), byte_thing: None,
196 // i32_thing: None, i64_thing: Some(12938492818) };
197 // let x_cmp = Xtruct { string_thing: Some("foo".to_owned()), byte_thing:
198 // Some(0), i32_thing: Some(0), i64_thing: Some(12938492818) }; // the C++
199 // server is responding correctly
Allen George8b96bfb2016-11-02 08:01:08 -0400200 // try!(verify_expected_result(client.test_struct(x_snd), x_cmp));
201 // }
202 //
203
204
205 println!("testNest"); // (FIXME: try Xtruct2 with optional values)
206 {
207 let x_snd = Xtruct2 {
208 byte_thing: Some(32),
Allen George0e22c362017-01-30 07:15:00 -0500209 struct_thing: Some(
210 Xtruct {
211 string_thing: Some("foo".to_owned()),
212 byte_thing: Some(1),
213 i32_thing: Some(324382098),
214 i64_thing: Some(12938492818),
215 },
216 ),
Allen George8b96bfb2016-11-02 08:01:08 -0400217 i32_thing: Some(293481098),
218 };
219 let x_cmp = Xtruct2 {
220 byte_thing: Some(32),
Allen George0e22c362017-01-30 07:15:00 -0500221 struct_thing: Some(
222 Xtruct {
223 string_thing: Some("foo".to_owned()),
224 byte_thing: Some(1),
225 i32_thing: Some(324382098),
226 i64_thing: Some(12938492818),
227 },
228 ),
Allen George8b96bfb2016-11-02 08:01:08 -0400229 i32_thing: Some(293481098),
230 };
231 verify_expected_result(client.test_nest(x_snd), x_cmp)?;
232 }
233
234 println!("testList");
235 {
236 let mut v_snd: Vec<i32> = Vec::new();
237 v_snd.push(29384);
238 v_snd.push(238);
239 v_snd.push(32498);
240
241 let mut v_cmp: Vec<i32> = Vec::new();
242 v_cmp.push(29384);
243 v_cmp.push(238);
244 v_cmp.push(32498);
245
246 verify_expected_result(client.test_list(v_snd), v_cmp)?;
247 }
248
249 println!("testSet");
250 {
251 let mut s_snd: BTreeSet<i32> = BTreeSet::new();
252 s_snd.insert(293481);
253 s_snd.insert(23);
254 s_snd.insert(3234);
255
256 let mut s_cmp: BTreeSet<i32> = BTreeSet::new();
257 s_cmp.insert(293481);
258 s_cmp.insert(23);
259 s_cmp.insert(3234);
260
261 verify_expected_result(client.test_set(s_snd), s_cmp)?;
262 }
263
264 println!("testMap");
265 {
266 let mut m_snd: BTreeMap<i32, i32> = BTreeMap::new();
267 m_snd.insert(2, 4);
268 m_snd.insert(4, 6);
269 m_snd.insert(8, 7);
270
271 let mut m_cmp: BTreeMap<i32, i32> = BTreeMap::new();
272 m_cmp.insert(2, 4);
273 m_cmp.insert(4, 6);
274 m_cmp.insert(8, 7);
275
276 verify_expected_result(client.test_map(m_snd), m_cmp)?;
277 }
278
279 println!("testStringMap");
280 {
281 let mut m_snd: BTreeMap<String, String> = BTreeMap::new();
282 m_snd.insert("2".to_owned(), "4_string".to_owned());
283 m_snd.insert("4".to_owned(), "6_string".to_owned());
284 m_snd.insert("8".to_owned(), "7_string".to_owned());
285
286 let mut m_rcv: BTreeMap<String, String> = BTreeMap::new();
287 m_rcv.insert("2".to_owned(), "4_string".to_owned());
288 m_rcv.insert("4".to_owned(), "6_string".to_owned());
289 m_rcv.insert("8".to_owned(), "7_string".to_owned());
290
291 verify_expected_result(client.test_string_map(m_snd), m_rcv)?;
292 }
293
294 // nested map
Allen George0e22c362017-01-30 07:15:00 -0500295 // expect : {-4 => {-4 => -4, -3 => -3, -2 => -2, -1 => -1, }, 4 => {1 => 1, 2
296 // => 2, 3 => 3, 4 => 4, }, }
Allen George8b96bfb2016-11-02 08:01:08 -0400297 println!("testMapMap");
298 {
299 let mut m_cmp_nested_0: BTreeMap<i32, i32> = BTreeMap::new();
300 for i in (-4 as i32)..0 {
301 m_cmp_nested_0.insert(i, i);
302 }
303 let mut m_cmp_nested_1: BTreeMap<i32, i32> = BTreeMap::new();
304 for i in 1..5 {
305 m_cmp_nested_1.insert(i, i);
306 }
307
308 let mut m_cmp: BTreeMap<i32, BTreeMap<i32, i32>> = BTreeMap::new();
309 m_cmp.insert(-4, m_cmp_nested_0);
310 m_cmp.insert(4, m_cmp_nested_1);
311
312 verify_expected_result(client.test_map_map(42), m_cmp)?;
313 }
314
315 println!("testMulti");
316 {
317 let mut m_snd: BTreeMap<i16, String> = BTreeMap::new();
318 m_snd.insert(1298, "fizz".to_owned());
319 m_snd.insert(-148, "buzz".to_owned());
320
321 let s_cmp = Xtruct {
322 string_thing: Some("Hello2".to_owned()),
323 byte_thing: Some(1),
324 i32_thing: Some(-123948),
325 i64_thing: Some(-19234123981),
326 };
327
Allen George0e22c362017-01-30 07:15:00 -0500328 verify_expected_result(
329 client.test_multi(1, -123948, -19234123981, m_snd, Numberz::EIGHT, 81),
330 s_cmp,
331 )?;
Allen George8b96bfb2016-11-02 08:01:08 -0400332 }
333
334 // Insanity
335 // returns:
336 // { 1 => { 2 => argument,
337 // 3 => argument,
338 // },
339 // 2 => { 6 => <empty Insanity struct>, },
340 // }
341 {
342 let mut arg_map_usermap: BTreeMap<Numberz, i64> = BTreeMap::new();
343 arg_map_usermap.insert(Numberz::ONE, 4289);
344 arg_map_usermap.insert(Numberz::EIGHT, 19);
345
346 let mut arg_vec_xtructs: Vec<Xtruct> = Vec::new();
Allen George0e22c362017-01-30 07:15:00 -0500347 arg_vec_xtructs.push(
348 Xtruct {
349 string_thing: Some("foo".to_owned()),
350 byte_thing: Some(8),
351 i32_thing: Some(29),
352 i64_thing: Some(92384),
353 },
354 );
355 arg_vec_xtructs.push(
356 Xtruct {
357 string_thing: Some("bar".to_owned()),
358 byte_thing: Some(28),
359 i32_thing: Some(2),
360 i64_thing: Some(-1281),
361 },
362 );
363 arg_vec_xtructs.push(
364 Xtruct {
365 string_thing: Some("baz".to_owned()),
366 byte_thing: Some(0),
367 i32_thing: Some(3948539),
368 i64_thing: Some(-12938492),
369 },
370 );
Allen George8b96bfb2016-11-02 08:01:08 -0400371
372 let mut s_cmp_nested_1: BTreeMap<Numberz, Insanity> = BTreeMap::new();
373 let insanity = Insanity {
374 user_map: Some(arg_map_usermap),
375 xtructs: Some(arg_vec_xtructs),
376 };
377 s_cmp_nested_1.insert(Numberz::TWO, insanity.clone());
378 s_cmp_nested_1.insert(Numberz::THREE, insanity.clone());
379
380 let mut s_cmp_nested_2: BTreeMap<Numberz, Insanity> = BTreeMap::new();
381 let empty_insanity = Insanity {
382 user_map: Some(BTreeMap::new()),
383 xtructs: Some(Vec::new()),
384 };
385 s_cmp_nested_2.insert(Numberz::SIX, empty_insanity);
386
387 let mut s_cmp: BTreeMap<UserId, BTreeMap<Numberz, Insanity>> = BTreeMap::new();
388 s_cmp.insert(1 as UserId, s_cmp_nested_1);
389 s_cmp.insert(2 as UserId, s_cmp_nested_2);
390
391 verify_expected_result(client.test_insanity(insanity.clone()), s_cmp)?;
392 }
393
394 println!("testException - remote throws Xception");
395 {
396 let r = client.test_exception("Xception".to_owned());
397 let x = match r {
398 Err(thrift::Error::User(ref e)) => {
399 match e.downcast_ref::<Xception>() {
400 Some(x) => Ok(x),
Allen George0e22c362017-01-30 07:15:00 -0500401 None => Err(thrift::Error::User("did not get expected Xception struct".into()),),
Allen George8b96bfb2016-11-02 08:01:08 -0400402 }
403 }
404 _ => Err(thrift::Error::User("did not get exception".into())),
405 }?;
406
407 let x_cmp = Xception {
408 error_code: Some(1001),
409 message: Some("Xception".to_owned()),
410 };
411
412 verify_expected_result(Ok(x), &x_cmp)?;
413 }
414
415 println!("testException - remote throws TApplicationException");
416 {
417 let r = client.test_exception("TException".to_owned());
418 match r {
419 Err(thrift::Error::Application(ref e)) => {
420 println!("received an {:?}", e);
421 Ok(())
422 }
423 _ => Err(thrift::Error::User("did not get exception".into())),
424 }?;
425 }
426
427 println!("testException - remote succeeds");
428 {
429 let r = client.test_exception("foo".to_owned());
430 match r {
431 Ok(_) => Ok(()),
432 _ => Err(thrift::Error::User("received an exception".into())),
433 }?;
434 }
435
436 println!("testMultiException - remote throws Xception");
437 {
438 let r = client.test_multi_exception("Xception".to_owned(), "ignored".to_owned());
439 let x = match r {
440 Err(thrift::Error::User(ref e)) => {
441 match e.downcast_ref::<Xception>() {
442 Some(x) => Ok(x),
Allen George0e22c362017-01-30 07:15:00 -0500443 None => Err(thrift::Error::User("did not get expected Xception struct".into()),),
Allen George8b96bfb2016-11-02 08:01:08 -0400444 }
445 }
446 _ => Err(thrift::Error::User("did not get exception".into())),
447 }?;
448
449 let x_cmp = Xception {
450 error_code: Some(1001),
451 message: Some("This is an Xception".to_owned()),
452 };
453
454 verify_expected_result(Ok(x), &x_cmp)?;
455 }
456
457 println!("testMultiException - remote throws Xception2");
458 {
459 let r = client.test_multi_exception("Xception2".to_owned(), "ignored".to_owned());
460 let x = match r {
461 Err(thrift::Error::User(ref e)) => {
462 match e.downcast_ref::<Xception2>() {
463 Some(x) => Ok(x),
Allen George0e22c362017-01-30 07:15:00 -0500464 None => Err(thrift::Error::User("did not get expected Xception struct".into()),),
Allen George8b96bfb2016-11-02 08:01:08 -0400465 }
466 }
467 _ => Err(thrift::Error::User("did not get exception".into())),
468 }?;
469
470 let x_cmp = Xception2 {
471 error_code: Some(2002),
Allen George0e22c362017-01-30 07:15:00 -0500472 struct_thing: Some(
473 Xtruct {
474 string_thing: Some("This is an Xception2".to_owned()),
475 // since this is an OPT_IN_REQ_OUT field the sender sets a default
476 byte_thing: Some(0),
477 // since this is an OPT_IN_REQ_OUT field the sender sets a default
478 i32_thing: Some(0),
479 // since this is an OPT_IN_REQ_OUT field the sender sets a default
480 i64_thing: Some(0),
481 },
482 ),
Allen George8b96bfb2016-11-02 08:01:08 -0400483 };
484
485 verify_expected_result(Ok(x), &x_cmp)?;
486 }
487
488 println!("testMultiException - remote succeeds");
489 {
490 let r = client.test_multi_exception("haha".to_owned(), "RETURNED".to_owned());
491 let x = match r {
Allen George0e22c362017-01-30 07:15:00 -0500492 Err(e) => Err(thrift::Error::User(format!("received an unexpected exception {:?}", e).into(),),),
Allen George8b96bfb2016-11-02 08:01:08 -0400493 _ => r,
494 }?;
495
496 let x_cmp = Xtruct {
497 string_thing: Some("RETURNED".to_owned()),
Allen George0e22c362017-01-30 07:15:00 -0500498 // since this is an OPT_IN_REQ_OUT field the sender sets a default
499 byte_thing: Some(0),
500 // since this is an OPT_IN_REQ_OUT field the sender sets a default
501 i32_thing: Some(0),
502 // since this is an OPT_IN_REQ_OUT field the sender sets a default
503 i64_thing: Some(0),
Allen George8b96bfb2016-11-02 08:01:08 -0400504 };
505
506 verify_expected_result(Ok(x), x_cmp)?;
507 }
508
509 println!("testOneWay - remote sleeps for 1 second");
510 {
511 client.test_oneway(1)?;
512 }
513
Allen George0e22c362017-01-30 07:15:00 -0500514 // final test to verify that the connection is still writable after the one-way
515 // call
Allen George8b96bfb2016-11-02 08:01:08 -0400516 client.test_void()
517}
518
Allen George0e22c362017-01-30 07:15:00 -0500519#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
520fn verify_expected_result<T: Debug + PartialEq + Sized>(
521 actual: Result<T, thrift::Error>,
522 expected: T,
523) -> Result<(), thrift::Error> {
Allen George8b96bfb2016-11-02 08:01:08 -0400524 match actual {
525 Ok(v) => {
526 if v == expected {
527 Ok(())
528 } else {
Allen George0e22c362017-01-30 07:15:00 -0500529 Err(thrift::Error::User(format!("expected {:?} but got {:?}", &expected, &v).into()),)
Allen George8b96bfb2016-11-02 08:01:08 -0400530 }
531 }
532 Err(e) => Err(e),
533 }
534}