// sync_failure.rs // // Various tests to ensure that failing components fail in a consistent way. use super::*; #[test] fn test_local_sync_failure() { // If the component exits cleanly, then the runtime exits cleanly, and the // test will finish const CODE: &'static str = " primitive immediate_failure_inside_sync() { u32[] only_allows_index_0 = { 1 }; while (true) sync { // note the infinite loop auto value = only_allows_index_0[1]; } } primitive immediate_failure_outside_sync() { u32[] only_allows_index_0 = { 1 }; auto never_gonna_get = only_allows_index_0[1]; while (true) sync {} } "; // let thing = TestTimer::new("local_sync_failure"); run_test_in_runtime(CODE, |api| { api.create_connector("", "immediate_failure_outside_sync", ValueGroup::new_stack(Vec::new())) .expect("create component"); api.create_connector("", "immediate_failure_inside_sync", ValueGroup::new_stack(Vec::new())) .expect("create component"); }) } const SHARED_SYNC_CODE: &'static str = " enum Location { BeforeSync, AfterPut, AfterGet, AfterSync, Never } primitive failing_at_location(in input, out output, Location loc) { u32[] failure_array = {}; while (true) { if (loc == Location::BeforeSync) failure_array[0]; sync { put(output, true); if (loc == Location::AfterPut) failure_array[0]; auto received = get(input); assert(received); if (loc == Location::AfterGet) failure_array[0]; } if (loc == Location::AfterSync) failure_array[0]; } } composite constructor_pair_a(Location loc) { channel output_a -> input_a; channel output_b -> input_b; new failing_at_location(input_b, output_a, loc); new failing_at_location(input_a, output_b, Location::Never); } composite constructor_pair_b(Location loc) { channel output_a -> input_a; channel output_b -> input_b; new failing_at_location(input_b, output_a, Location::Never); new failing_at_location(input_a, output_b, loc); } composite constructor_ring(u32 ring_size, u32 fail_a, Location loc_a, u32 fail_b, Location loc_b) { channel output_first -> input_old; channel output_cur -> input_new; u32 ring_index = 0; while (ring_index < ring_size) { auto cur_loc = Location::Never; if (ring_index == fail_a) cur_loc = loc_a; if (ring_index == fail_b) cur_loc = loc_b; new failing_at_location(input_old, output_cur, cur_loc); if (ring_index == ring_size - 2) { // Don't create a new channel, join up the last one output_cur = output_first; input_old = input_new; } else if (ring_index != ring_size - 1) { channel output_fresh -> input_fresh; input_old = input_new; output_cur = output_fresh; input_new = input_fresh; } ring_index += 1; } } "; #[test] fn test_shared_sync_failure_pair_variant_a() { // One fails, the other one should somehow detect it and fail as well. This // variant constructs the failing component first. run_test_in_runtime(SHARED_SYNC_CODE, |api| { for variant in 0..4 { // all `Location` enum variants, except `Never`. // Create the channels api.create_connector("", "constructor_pair_a", ValueGroup::new_stack(vec![ Value::Enum(variant) ])).expect("create connector"); } }) } #[test] fn test_shared_sync_failure_pair_variant_b() { // One fails, the other one should somehow detect it and fail as well. This // variant constructs the successful component first. run_test_in_runtime(SHARED_SYNC_CODE, |api| { for variant in 0..4 { api.create_connector("", "constructor_pair_b", ValueGroup::new_stack(vec![ Value::Enum(variant) ])).expect("create connector"); } }) } #[test] fn test_shared_sync_failure_ring_variant_a() { // Only one component in the ring should fail const RING_SIZE: u32 = 4; run_test_in_runtime(SHARED_SYNC_CODE, |api| { for variant in 0..4 { api.create_connector("", "constructor_ring", ValueGroup::new_stack(vec![ Value::UInt32(RING_SIZE), Value::UInt32(RING_SIZE / 2), Value::Enum(variant), // fail "halfway" the ring Value::UInt32(RING_SIZE), Value::Enum(0), // never occurs, index is equal to ring size ])).expect("create connector"); } }) }