@@ -106,3 +106,209 @@ impl VirtualMachine {
106106 Ok ( ( ) )
107107 }
108108}
109+
110+ #[ cfg( test) ]
111+ mod tests {
112+ use p3_baby_bear:: BabyBear ;
113+ use p3_field:: PrimeCharacteristicRing ;
114+
115+ use super :: * ;
116+ use crate :: {
117+ memory:: { address:: MemoryAddress , val:: MemoryValue } ,
118+ types:: instruction:: { MemOrConstant , Operation } ,
119+ } ;
120+
121+ type F = BabyBear ;
122+
123+ /// Creates and configures a `VirtualMachine` instance for testing purposes.
124+ ///
125+ /// This function streamlines the setup process for tests by initializing the VM
126+ /// with a specific state, including
127+ /// - the program counter (`pc`),
128+ /// - the frame pointer (`fp`),
129+ /// - any required initial memory values.
130+ fn setup_vm (
131+ pc : MemoryAddress ,
132+ fp : MemoryAddress ,
133+ initial_memory : & [ ( MemoryAddress , MemoryValue < F > ) ] ,
134+ ) -> VirtualMachine {
135+ // Create a new VM with default values.
136+ let mut vm = VirtualMachine :: default ( ) ;
137+ // Set the initial program counter and frame pointer.
138+ vm. run_context . pc = pc;
139+ vm. run_context . fp = fp;
140+ // Iterate through the provided initial memory layout.
141+ for ( addr, val) in initial_memory {
142+ // Ensure enough memory segments are allocated to accommodate the address.
143+ while vm. memory_manager . num_segments ( ) <= addr. segment_index {
144+ vm. memory_manager . add ( ) ;
145+ }
146+ // Insert the value at the specified address, panicking on failure for test simplicity.
147+ vm. memory_manager . memory . insert ( * addr, val. clone ( ) ) . unwrap ( ) ;
148+ }
149+ // Return the fully configured VM.
150+ vm
151+ }
152+
153+ #[ test]
154+ fn test_update_pc_for_non_jnz_instruction ( ) {
155+ // Setup: Initialize a VM with the PC at (0, 5).
156+ let mut vm = setup_vm ( MemoryAddress :: new ( 0 , 5 ) , MemoryAddress :: new ( 1 , 0 ) , & [ ] ) ;
157+ // Define a non-jump instruction (e.g., a computation).
158+ let instruction = Instruction :: Computation :: < F > {
159+ operation : Operation :: Add ,
160+ arg_a : MemOrConstant :: Constant ( F :: ONE ) ,
161+ arg_b : MemOrFp :: Fp ,
162+ res : MemOrConstant :: MemoryAfterFp { shift : 0 } ,
163+ } ;
164+ // Execute: Update the PC based on this instruction.
165+ vm. update_pc ( & instruction) . unwrap ( ) ;
166+ // Verify: The PC should increment by 1, as it's not a JNZ instruction.
167+ assert_eq ! ( vm. run_context. pc, MemoryAddress :: new( 0 , 6 ) ) ;
168+ }
169+
170+ #[ test]
171+ fn test_update_pc_jnz_condition_zero ( ) {
172+ // Setup: Initialize PC and FP registers.
173+ let pc = MemoryAddress :: new ( 0 , 10 ) ;
174+ let fp = MemoryAddress :: new ( 1 , 5 ) ;
175+ // Pre-load memory with a zero value at the address `fp + 1`, which will be our condition.
176+ let mut vm = setup_vm ( pc, fp, & [ ( ( fp + 1 ) . unwrap ( ) , MemoryValue :: Int ( F :: ZERO ) ) ] ) ;
177+ // Define a JNZ instruction where the condition points to the zero value.
178+ let instruction = Instruction :: JumpIfNotZero :: < F > {
179+ condition : MemOrConstant :: MemoryAfterFp { shift : 1 } ,
180+ dest : MemOrConstant :: Constant ( F :: from_u64 ( 99 ) ) , // This destination should be ignored.
181+ updated_fp : MemOrFp :: Fp ,
182+ } ;
183+ // Execute: Update the PC.
184+ vm. update_pc ( & instruction) . unwrap ( ) ;
185+ // Verify: Since the condition is zero, the jump is not taken, and the PC increments by 1.
186+ assert_eq ! ( vm. run_context. pc, MemoryAddress :: new( 0 , 11 ) ) ;
187+ }
188+
189+ #[ test]
190+ fn test_update_pc_jnz_condition_nonzero_jumps ( ) {
191+ // Setup: Initialize PC and FP registers.
192+ let pc = MemoryAddress :: new ( 0 , 10 ) ;
193+ let fp = MemoryAddress :: new ( 1 , 5 ) ;
194+ // Define the target address for the jump.
195+ let jump_target = MemoryAddress :: new ( 2 , 20 ) ;
196+ // Pre-load memory with a non-zero condition value and the jump target address.
197+ let mut vm = setup_vm (
198+ pc,
199+ fp,
200+ & [
201+ // The condition value (non-zero).
202+ ( ( fp + 1 ) . unwrap ( ) , MemoryValue :: Int ( F :: from_u64 ( 42 ) ) ) ,
203+ // The destination address for the jump.
204+ ( ( fp + 2 ) . unwrap ( ) , MemoryValue :: Address ( jump_target) ) ,
205+ ] ,
206+ ) ;
207+ // Define a JNZ instruction pointing to the condition and destination.
208+ let instruction = Instruction :: JumpIfNotZero :: < F > {
209+ condition : MemOrConstant :: MemoryAfterFp { shift : 1 } ,
210+ dest : MemOrConstant :: MemoryAfterFp { shift : 2 } ,
211+ updated_fp : MemOrFp :: Fp ,
212+ } ;
213+ // Execute: Update the PC.
214+ vm. update_pc ( & instruction) . unwrap ( ) ;
215+ // Verify: Since the condition is non-zero, the PC should be updated to the jump target.
216+ assert_eq ! ( vm. run_context. pc, jump_target) ;
217+ }
218+
219+ #[ test]
220+ fn test_update_pc_jnz_condition_is_address_fails ( ) {
221+ // Setup: Initialize PC and FP.
222+ let pc = MemoryAddress :: new ( 0 , 10 ) ;
223+ let fp = MemoryAddress :: new ( 1 , 5 ) ;
224+ // Pre-load memory with an Address where an integer condition is expected.
225+ let mut vm = setup_vm (
226+ pc,
227+ fp,
228+ & [ (
229+ ( fp + 1 ) . unwrap ( ) ,
230+ MemoryValue :: Address ( MemoryAddress :: new ( 8 , 8 ) ) ,
231+ ) ] ,
232+ ) ;
233+ // Define a JNZ instruction where the condition points to the address.
234+ let instruction = Instruction :: JumpIfNotZero :: < F > {
235+ condition : MemOrConstant :: MemoryAfterFp { shift : 1 } ,
236+ dest : MemOrConstant :: Constant ( F :: ONE ) ,
237+ updated_fp : MemOrFp :: Fp ,
238+ } ;
239+ // Execute: Attempt to update the PC.
240+ let result = vm. update_pc ( & instruction) ;
241+ // Verify: The operation should fail because a condition must be an integer, not an address.
242+ assert ! ( matches!(
243+ result,
244+ Err ( VirtualMachineError :: Memory ( MemoryError :: ExpectedInteger ) )
245+ ) ) ;
246+ }
247+
248+ #[ test]
249+ fn test_update_fp_jnz_regular_update ( ) {
250+ // Setup: Initialize the FP to a known address.
251+ let fp = MemoryAddress :: new ( 1 , 5 ) ;
252+ let mut vm = setup_vm ( MemoryAddress :: new ( 0 , 0 ) , fp, & [ ] ) ;
253+ // Define a JNZ instruction that specifies the FP should not change (`MemOrFp::Fp`).
254+ let instruction = Instruction :: JumpIfNotZero :: < F > {
255+ condition : MemOrConstant :: Constant ( F :: ONE ) ,
256+ dest : MemOrConstant :: Constant ( F :: ONE ) ,
257+ updated_fp : MemOrFp :: Fp ,
258+ } ;
259+ // Execute: Update the FP.
260+ vm. update_fp ( & instruction) . unwrap ( ) ;
261+ // Verify: The FP should remain unchanged.
262+ assert_eq ! ( vm. run_context. fp, fp) ;
263+ }
264+
265+ #[ test]
266+ fn test_update_fp_jnz_dst_update ( ) {
267+ // Setup: Initialize the FP and define a new address for it to be updated to.
268+ let fp = MemoryAddress :: new ( 1 , 5 ) ;
269+ let new_fp = MemoryAddress :: new ( 2 , 0 ) ;
270+ // Pre-load memory with the new FP address at `fp + 3`.
271+ let mut vm = setup_vm (
272+ MemoryAddress :: new ( 0 , 0 ) ,
273+ fp,
274+ & [ ( ( fp + 3 ) . unwrap ( ) , MemoryValue :: Address ( new_fp) ) ] ,
275+ ) ;
276+ // Define a JNZ instruction where `updated_fp` points to the new address in memory.
277+ let instruction = Instruction :: JumpIfNotZero :: < F > {
278+ condition : MemOrConstant :: Constant ( F :: ONE ) ,
279+ dest : MemOrConstant :: Constant ( F :: ONE ) ,
280+ updated_fp : MemOrFp :: MemoryAfterFp { shift : 3 } ,
281+ } ;
282+ // Execute: Update the FP.
283+ vm. update_fp ( & instruction) . unwrap ( ) ;
284+ // Verify: The FP should be updated to the new address.
285+ assert_eq ! ( vm. run_context. fp, new_fp) ;
286+ }
287+
288+ #[ test]
289+ fn test_update_fp_jnz_dst_is_int_fails ( ) {
290+ // Setup: Initialize the FP.
291+ let fp = MemoryAddress :: new ( 1 , 5 ) ;
292+ // Pre-load memory with an integer value where a new FP address is expected.
293+ let mut vm = setup_vm (
294+ MemoryAddress :: new ( 0 , 0 ) ,
295+ fp,
296+ & [ ( ( fp + 3 ) . unwrap ( ) , MemoryValue :: Int ( F :: from_u64 ( 99 ) ) ) ] ,
297+ ) ;
298+ // Define a JNZ instruction where `updated_fp` points to this integer value.
299+ let instruction = Instruction :: JumpIfNotZero :: < F > {
300+ condition : MemOrConstant :: Constant ( F :: ONE ) ,
301+ dest : MemOrConstant :: Constant ( F :: ONE ) ,
302+ updated_fp : MemOrFp :: MemoryAfterFp { shift : 3 } ,
303+ } ;
304+ // Execute: Attempt to update the FP.
305+ let result = vm. update_fp ( & instruction) ;
306+ // Verify: The operation should fail because the new FP value must be an address, not an integer.
307+ assert ! ( matches!(
308+ result,
309+ Err ( VirtualMachineError :: Memory (
310+ MemoryError :: ExpectedMemoryAddress
311+ ) )
312+ ) ) ;
313+ }
314+ }
0 commit comments