1 //! Generating sequences of Wasmtime API calls. 2 //! 3 //! We only generate *valid* sequences of API calls. To do this, we keep track 4 //! of what objects we've already created in earlier API calls via the `Scope` 5 //! struct. 6 //! 7 //! To generate even-more-pathological sequences of API calls, we use [swarm 8 //! testing]: 9 //! 10 //! > In swarm testing, the usual practice of potentially including all features 11 //! > in every test case is abandoned. Rather, a large “swarm” of randomly 12 //! > generated configurations, each of which omits some features, is used, with 13 //! > configurations receiving equal resources. 14 //! 15 //! [swarm testing]: https://www.cs.utah.edu/~regehr/papers/swarm12.pdf 16 17 use arbitrary::{Arbitrary, Unstructured}; 18 use std::collections::BTreeSet; 19 20 #[derive(Arbitrary, Debug)] 21 struct Swarm { 22 config_debug_info: bool, 23 module_new: bool, 24 module_drop: bool, 25 instance_new: bool, 26 instance_drop: bool, 27 call_exported_func: bool, 28 } 29 30 /// A call to one of Wasmtime's public APIs. 31 #[derive(Arbitrary, Clone, Debug)] 32 #[allow(missing_docs)] 33 pub enum ApiCall { 34 ConfigNew, 35 ConfigDebugInfo(bool), 36 EngineNew, 37 StoreNew, 38 ModuleNew { id: usize, wasm: super::WasmOptTtf }, 39 ModuleDrop { id: usize }, 40 InstanceNew { id: usize, module: usize }, 41 InstanceDrop { id: usize }, 42 CallExportedFunc { instance: usize, nth: usize }, 43 } 44 use ApiCall::*; 45 46 #[derive(Default)] 47 struct Scope { 48 id_counter: usize, 49 modules: BTreeSet<usize>, 50 instances: BTreeSet<usize>, 51 } 52 53 impl Scope { 54 fn next_id(&mut self) -> usize { 55 let id = self.id_counter; 56 self.id_counter = id + 1; 57 id 58 } 59 } 60 61 /// A sequence of API calls. 62 #[derive(Debug)] 63 pub struct ApiCalls { 64 /// The API calls. 65 pub calls: Vec<ApiCall>, 66 } 67 68 impl Arbitrary for ApiCalls { 69 fn arbitrary(input: &mut Unstructured) -> arbitrary::Result<Self> { 70 let swarm = Swarm::arbitrary(input)?; 71 let mut calls = vec![]; 72 73 arbitrary_config(input, &swarm, &mut calls)?; 74 calls.push(EngineNew); 75 calls.push(StoreNew); 76 77 let mut scope = Scope::default(); 78 79 for _ in 0..input.arbitrary_len::<ApiCall>()? { 80 let mut choices: Vec<fn(_, &mut Scope) -> arbitrary::Result<ApiCall>> = vec![]; 81 82 if swarm.module_new { 83 choices.push(|input, scope| { 84 let id = scope.next_id(); 85 scope.modules.insert(id); 86 let wasm = super::WasmOptTtf::arbitrary(input)?; 87 Ok(ModuleNew { id, wasm }) 88 }); 89 } 90 if swarm.module_drop && !scope.modules.is_empty() { 91 choices.push(|input, scope| { 92 let modules: Vec<_> = scope.modules.iter().cloned().collect(); 93 let id = *input.choose(&modules)?; 94 scope.modules.remove(&id); 95 Ok(ModuleDrop { id }) 96 }); 97 } 98 if swarm.instance_new && !scope.modules.is_empty() { 99 choices.push(|input, scope| { 100 let modules: Vec<_> = scope.modules.iter().cloned().collect(); 101 let module = *input.choose(&modules)?; 102 let id = scope.next_id(); 103 scope.instances.insert(id); 104 Ok(InstanceNew { id, module }) 105 }); 106 } 107 if swarm.instance_drop && !scope.instances.is_empty() { 108 choices.push(|input, scope| { 109 let instances: Vec<_> = scope.instances.iter().cloned().collect(); 110 let id = *input.choose(&instances)?; 111 scope.instances.remove(&id); 112 Ok(InstanceDrop { id }) 113 }); 114 } 115 if swarm.call_exported_func && !scope.instances.is_empty() { 116 choices.push(|input, scope| { 117 let instances: Vec<_> = scope.instances.iter().cloned().collect(); 118 let instance = *input.choose(&instances)?; 119 let nth = usize::arbitrary(input)?; 120 Ok(CallExportedFunc { instance, nth }) 121 }); 122 } 123 124 if choices.is_empty() { 125 break; 126 } 127 let c = input.choose(&choices)?; 128 calls.push(c(input, &mut scope)?); 129 } 130 131 Ok(ApiCalls { calls }) 132 } 133 } 134 135 fn arbitrary_config( 136 input: &mut Unstructured, 137 swarm: &Swarm, 138 calls: &mut Vec<ApiCall>, 139 ) -> arbitrary::Result<()> { 140 calls.push(ConfigNew); 141 142 if swarm.config_debug_info && bool::arbitrary(input)? { 143 calls.push(ConfigDebugInfo(bool::arbitrary(input)?)); 144 } 145 146 // TODO: flags, features, and compilation strategy. 147 148 Ok(()) 149 } 150