use crate::protocol::*; use crate::protocol::eval::*; use crate::runtime2::runtime::*; use crate::runtime2::component::{CompCtx, CompPDL}; fn create_component(rt: &Runtime, module_name: &str, routine_name: &str, args: ValueGroup) { let prompt = rt.inner.protocol.new_component( module_name.as_bytes(), routine_name.as_bytes(), args ).expect("create prompt"); let reserved = rt.inner.start_create_pdl_component(); let ctx = CompCtx::new(&reserved); let component = Box::new(CompPDL::new(prompt, 0)); let (key, _) = rt.inner.finish_create_pdl_component(reserved, component, ctx, false); rt.inner.enqueue_work(key); } fn no_args() -> ValueGroup { ValueGroup::new_stack(Vec::new()) } #[test] fn test_component_creation() { let pd = ProtocolDescription::parse(b" primitive nothing_at_all() { s32 a = 5; auto b = 5 + a; } ").expect("compilation"); let rt = Runtime::new(1, true, pd).unwrap(); for _i in 0..20 { create_component(&rt, "", "nothing_at_all", no_args()); } } #[test] fn test_component_communication() { let pd = ProtocolDescription::parse(b" primitive sender(out o, u32 outside_loops, u32 inside_loops) { u32 outside_index = 0; while (outside_index < outside_loops) { u32 inside_index = 0; sync while (inside_index < inside_loops) { put(o, inside_index); inside_index += 1; } outside_index += 1; } } primitive receiver(in i, u32 outside_loops, u32 inside_loops) { u32 outside_index = 0; while (outside_index < outside_loops) { u32 inside_index = 0; sync while (inside_index < inside_loops) { auto val = get(i); while (val != inside_index) {} // infinite loop if incorrect value is received inside_index += 1; } outside_index += 1; } } composite constructor() { channel o_orom -> i_orom; channel o_mrom -> i_mrom; channel o_ormm -> i_ormm; channel o_mrmm -> i_mrmm; // one round, one message per round new sender(o_orom, 1, 1); new receiver(i_orom, 1, 1); // multiple rounds, one message per round new sender(o_mrom, 5, 1); new receiver(i_mrom, 5, 1); // one round, multiple messages per round new sender(o_ormm, 1, 5); new receiver(i_ormm, 1, 5); // multiple rounds, multiple messages per round new sender(o_mrmm, 5, 5); new receiver(i_mrmm, 5, 5); }").expect("compilation"); let rt = Runtime::new(3, true, pd).unwrap(); create_component(&rt, "", "constructor", no_args()); } #[test] fn test_intermediate_messenger() { let pd = ProtocolDescription::parse(b" primitive receiver(in rx, u32 num) { auto index = 0; while (index < num) { sync { auto v = get(rx); } index += 1; } } primitive middleman(in rx, out tx, u32 num) { auto index = 0; while (index < num) { sync { put(tx, get(rx)); } index += 1; } } primitive sender(out tx, u32 num) { auto index = 0; while (index < num) { sync put(tx, 1337); index += 1; } } composite constructor_template() { auto num = 0; channel tx_a -> rx_a; channel tx_b -> rx_b; new sender(tx_a, 3); new middleman(rx_a, tx_b, 3); new receiver(rx_b, 3); } composite constructor() { new constructor_template(); new constructor_template(); new constructor_template(); new constructor_template(); new constructor_template(); new constructor_template(); } ").expect("compilation"); let rt = Runtime::new(3, true, pd).unwrap(); create_component(&rt, "", "constructor", no_args()); } #[test] fn test_simple_select() { let pd = ProtocolDescription::parse(b" func infinite_assert(T val, T expected) -> () { while (val != expected) { print(\"nope!\"); } return (); } primitive receiver(in in_a, in in_b, u32 num_sends) { auto num_from_a = 0; auto num_from_b = 0; while (num_from_a + num_from_b < 2 * num_sends) { sync select { auto v = get(in_a) -> { print(\"got something from A\"); auto _ = infinite_assert(v, num_from_a); num_from_a += 1; } auto v = get(in_b) -> { print(\"got something from B\"); auto _ = infinite_assert(v, num_from_b); num_from_b += 1; } } } } primitive sender(out tx, u32 num_sends) { auto index = 0; while (index < num_sends) { sync { put(tx, index); index += 1; } } } composite constructor() { auto num_sends = 1; channel tx_a -> rx_a; channel tx_b -> rx_b; new sender(tx_a, num_sends); new receiver(rx_a, rx_b, num_sends); new sender(tx_b, num_sends); } ").expect("compilation"); let rt = Runtime::new(3, true, pd).unwrap(); create_component(&rt, "", "constructor", no_args()); } #[test] fn test_unguarded_select() { let pd = ProtocolDescription::parse(b" primitive constructor_outside_select() { u32 index = 0; while (index < 5) { sync select { auto v = () -> print(\"hello\"); } index += 1; } } primitive constructor_inside_select() { u32 index = 0; while (index < 5) { sync select { auto v = () -> index += 1; } } } ").expect("compilation"); let rt = Runtime::new(3, false, pd).unwrap(); create_component(&rt, "", "constructor_outside_select", no_args()); create_component(&rt, "", "constructor_inside_select", no_args()); } #[test] fn test_empty_select() { let pd = ProtocolDescription::parse(b" primitive constructor() { u32 index = 0; while (index < 5) { sync select {} index += 1; } } ").expect("compilation"); let rt = Runtime::new(3, false, pd).unwrap(); create_component(&rt, "", "constructor", no_args()); } #[test] fn test_random_u32_temporary_thingo() { let pd = ProtocolDescription::parse(b" import std.random::random_u32; primitive random_taker(in generator, u32 num_values) { auto i = 0; while (i < num_values) { sync { auto a = get(generator); } i += 1; } } composite constructor() { channel tx -> rx; auto num_values = 25; new random_u32(tx, 1, 100, num_values); new random_taker(rx, num_values); } ").expect("compilation"); let rt = Runtime::new(1, true, pd).unwrap(); create_component(&rt, "", "constructor", no_args()); } #[test] fn test_tcp_socket_http_request() { let _pd = ProtocolDescription::parse(b" import std.internet::*; primitive requester(out cmd_tx, in data_rx) { print(\"*** TCPSocket: Sending request\"); sync { put(cmd_tx, Cmd::Send(b\"GET / HTTP/1.1\\r\\n\\r\\n\")); } print(\"*** TCPSocket: Receiving response\"); auto buffer = {}; auto done_receiving = false; sync while (!done_receiving) { put(cmd_tx, Cmd::Receive); auto data = get(data_rx); buffer @= data; // Completely crap detection of end-of-document. But here we go, we // try to detect the trailing . Proper way would be to parse // for 'content-length' or 'content-encoding' s32 index = 0; s32 partial_length = cast(length(data) - 7); while (index < partial_length) { // No string conversion yet, so check byte buffer one byte at // a time. auto c1 = data[index]; if (c1 == cast('<')) { auto c2 = data[index + 1]; auto c3 = data[index + 2]; auto c4 = data[index + 3]; auto c5 = data[index + 4]; auto c6 = data[index + 5]; auto c7 = data[index + 6]; if ( // i.e. if (data[index..] == '' c2 == cast('/') && c3 == cast('h') && c4 == cast('t') && c5 == cast('m') && c6 == cast('l') && c7 == cast('>') ) { print(\"*** TCPSocket: Detected \"); put(cmd_tx, Cmd::Finish); done_receiving = true; } } index += 1; } } print(\"*** TCPSocket: Requesting shutdown\"); sync { put(cmd_tx, Cmd::Shutdown); } } composite main() { channel cmd_tx -> cmd_rx; channel data_tx -> data_rx; new tcp_client({142, 250, 179, 163}, 80, cmd_rx, data_tx); // port 80 of google new requester(cmd_tx, data_rx); } ").expect("compilation"); // This test is disabled because it performs a HTTP request to google. // let rt = Runtime::new(1, true, pd).unwrap(); // create_component(&rt, "", "main", no_args()); } #[test] fn test_sending_receiving_union() { let pd = ProtocolDescription::parse(b" union Cmd { Set(u8[]), Get, Shutdown, } primitive database(in rx, out tx) { auto stored = {}; auto done = false; while (!done) { sync { auto command = get(rx); if (let Cmd::Set(bytes) = command) { print(\"database: storing value\"); stored = bytes; } else if (let Cmd::Get = command) { print(\"database: returning value\"); put(tx, stored); } else if (let Cmd::Shutdown = command) { print(\"database: shutting down\"); done = true; } else while (true) print(\"impossible\"); // no other case possible } } } primitive client(out tx, in rx, u32 num_rounds) { auto round = 0; while (round < num_rounds) { auto set_value = b\"hello there\"; print(\"client: putting a value\"); sync put(tx, Cmd::Set(set_value)); auto retrieved = {}; print(\"client: retrieving what was sent\"); sync { put(tx, Cmd::Get); retrieved = get(rx); } if (set_value != retrieved) while (true) print(\"wrong!\"); round += 1; } sync put(tx, Cmd::Shutdown); } composite main() { auto num_rounds = 5; channel cmd_tx -> cmd_rx; channel data_tx -> data_rx; new database(cmd_rx, data_tx); new client(cmd_tx, data_rx, num_rounds); } ").expect("compilation"); let rt = Runtime::new(1, false, pd).unwrap(); create_component(&rt, "", "main", no_args()); }