use asmachina::MachineW;
use lexariel::tokenize;
use parseid::parse;
use hephasm::assemble_program;
use std::process::Command;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_complete_pipeline_simple() {
let source = r#"
start:
POB data
WYJSCIE
STP
data: RST 42
"#;
let tokens = tokenize(source).expect("Lexing failed");
assert!(!tokens.is_empty());
let ast = parse(tokens).expect("Parsing failed");
assert!(!ast.elements.is_empty());
let machine_code = assemble_program(&ast).expect("Assembly failed");
assert!(!machine_code.is_empty());
let mut machine = MachineW::new();
machine.load_program(&machine_code).expect("Loading failed");
machine.run().expect("Execution failed");
assert_eq!(machine.is_running, false);
assert_eq!(machine.get_output_buffer(), &[42]);
}
#[test]
fn test_complete_pipeline_with_arithmetic() {
let source = r#"
start:
POB num1
DOD num2
WYJSCIE
STP
num1: RST 25
num2: RST 17
"#;
let tokens = tokenize(source).unwrap();
let ast = parse(tokens).unwrap();
let machine_code = assemble_program(&ast).unwrap();
let mut machine = MachineW::new();
machine.load_program(&machine_code).unwrap();
machine.run().unwrap();
assert_eq!(machine.get_output_buffer(), &[42]); assert_eq!(machine.ak, 42);
}
#[test]
fn test_complete_pipeline_with_jumps() {
let source = r#"
start:
POB counter
DOD one
ŁAD counter
POB counter ; Reload updated counter
SOM start ; Jump back to start if counter < 0
end:
POB counter
WYJSCIE
STP
counter: RST -3
one: RST 1
"#;
let tokens = tokenize(source).unwrap();
let ast = parse(tokens).unwrap();
let machine_code = assemble_program(&ast).unwrap();
let mut machine = MachineW::new();
machine.load_program(&machine_code).unwrap();
machine.run().unwrap();
assert_eq!(machine.get_output_buffer(), &[0]);
}
#[test]
fn test_complete_pipeline_with_stack() {
let source = r#"
start:
POB val1
SDP ; Push val1
POB val2
SDP ; Push val2
PZS ; Pop val2
PZS ; Pop val1
DOD val2 ; Add val2 (still in memory)
WYJSCIE
STP
val1: RST 10
val2: RST 30
"#;
let tokens = tokenize(source).unwrap();
let ast = parse(tokens).unwrap();
let machine_code = assemble_program(&ast).unwrap();
let mut machine = MachineW::new();
machine.load_program(&machine_code).unwrap();
machine.run().unwrap();
assert_eq!(machine.get_output_buffer(), &[40]); }
#[test]
fn test_complete_pipeline_with_macros() {
let source = r#"
MAKRO add_and_output val1 val2
POB val1
DOD val2
WYJSCIE
KONM
start:
add_and_output data1 data2
STP
data1: RST 15
data2: RST 25
"#;
let tokens = tokenize(source).unwrap();
let ast = parse(tokens).unwrap();
let machine_code = assemble_program(&ast).unwrap();
let mut machine = MachineW::new();
machine.load_program(&machine_code).unwrap();
machine.run().unwrap();
assert_eq!(machine.get_output_buffer(), &[40]); }
#[test]
fn test_all_instructions_integration() {
let source = r#"
start:
; Test arithmetic
POB val1 ; Load 10
DOD val2 ; Add 5 -> 15
ODE val3 ; Sub 3 -> 12
ŁAD result ; Store result
; Test stack
SDP ; Push 12
POB val4 ; Load 8
PZS ; Pop 12
DOD val4 ; Add 8 -> 20
; Test output
WYJSCIE ; Output 20
; Test jump
SOB end
end:
STP
val1: RST 10
val2: RST 5
val3: RST 3
val4: RST 8
result: RPA
"#;
let tokens = tokenize(source).unwrap();
let ast = parse(tokens).unwrap();
let machine_code = assemble_program(&ast).unwrap();
let mut machine = MachineW::new();
machine.load_program(&machine_code).unwrap();
machine.run().unwrap();
assert_eq!(machine.get_output_buffer(), &[20]);
assert_eq!(machine.ak, 20);
assert_eq!(machine.is_running, false);
assert_eq!(machine.read_memory(machine_code.len() as u16 - 1).unwrap(), 12);
}
#[test]
fn test_interrupt_handling_integration() {
let source = r#"
start:
POB data
SDP ; Push data to stack
DNS ; Disable interrupts
POB data
WYJSCIE
STP
data: RST 123
"#;
let tokens = tokenize(source).unwrap();
let ast = parse(tokens).unwrap();
let machine_code = assemble_program(&ast).unwrap();
let mut machine = MachineW::new();
machine.load_program(&machine_code).unwrap();
machine.run().unwrap();
assert_eq!(machine.get_output_buffer(), &[123]);
assert_eq!(machine.interrupts_enabled, false); }
#[test]
fn test_memory_bounds_integration() {
let source = r#"
POB 0
STP
"#;
let tokens = tokenize(source).unwrap();
let ast = parse(tokens).unwrap();
let machine_code = assemble_program(&ast).unwrap();
let mut machine = MachineW::new();
machine.load_program(&machine_code).unwrap();
let result = machine.run();
assert!(result.is_ok());
}
#[test]
fn test_large_program_integration() {
let mut source = String::from("start:\n");
for i in 0..100 {
source.push_str(&format!(" DOD data_{}\n", i));
}
source.push_str(" WYJSCIE\n");
source.push_str(" STP\n\n");
for i in 0..100 {
source.push_str(&format!("data_{}: RST {}\n", i, i + 1));
}
let tokens = tokenize(&source).unwrap();
let ast = parse(tokens).unwrap();
let machine_code = assemble_program(&ast).unwrap();
let mut machine = MachineW::new();
machine.load_program(&machine_code).unwrap();
machine.run().unwrap();
assert_eq!(machine.get_output_buffer(), &[5050]);
}
#[test]
fn test_full_pipeline_extended_instructions() {
let temp_dir = TempDir::new().unwrap();
let source_file = temp_dir.path().join("pipeline_test.asmod");
let binary_file = temp_dir.path().join("pipeline_test.bin");
let disasm_file = temp_dir.path().join("pipeline_test_disasm.asmod");
let original_program = r#"
; Pipeline test with extended instructions
start:
POB value1 ; load 12
MNO value2 ; multiply by 5 = 60
DZI #4 ; divide by 4 = 15
MOD #8 ; modulo 8 = 7
WYJSCIE ; output 7
STP
value1: RST 12
value2: RST 5
"#;
fs::write(&source_file, original_program).unwrap();
let assemble_output = Command::new("cargo")
.args(&["run", "--", "assemble", "--extended", source_file.to_str().unwrap(), "-o", binary_file.to_str().unwrap()])
.output()
.expect("Failed to execute assembler");
assert!(assemble_output.status.success(), "Assembly failed: {}", String::from_utf8_lossy(&assemble_output.stderr));
assert!(binary_file.exists(), "Binary file not created");
let run_output = Command::new("cargo")
.args(&["run", "--", "run", "--extended", source_file.to_str().unwrap()])
.output()
.expect("Failed to execute run command");
assert!(run_output.status.success(), "Run failed: {}", String::from_utf8_lossy(&run_output.stderr));
let stdout = String::from_utf8_lossy(&run_output.stdout);
assert!(stdout.contains("7"), "Expected output '7', got: {}", stdout);
let disasm_output = Command::new("cargo")
.args(&["run", "--", "disassemble", binary_file.to_str().unwrap(), "-o", disasm_file.to_str().unwrap()])
.output()
.expect("Failed to execute disassembler");
assert!(disasm_output.status.success(), "Disassembly failed: {}", String::from_utf8_lossy(&disasm_output.stderr));
assert!(disasm_file.exists(), "Disassembly file not created");
let disassembled_content = fs::read_to_string(&disasm_file).unwrap();
assert!(disassembled_content.contains("MNO"), "Disassembly should contain MNO");
assert!(disassembled_content.contains("DZI"), "Disassembly should contain DZI");
assert!(disassembled_content.contains("MOD"), "Disassembly should contain MOD");
let has_immediate = disassembled_content.contains("#4") || disassembled_content.contains("#8") ||
disassembled_content.contains("4") || disassembled_content.contains("8");
assert!(has_immediate, "Disassembly should contain immediate values");
}
#[test]
fn test_error_propagation_extended_mode() {
let temp_dir = TempDir::new().unwrap();
let source_file = temp_dir.path().join("error_test.asmod");
let program_with_error = r#"
start:
POB value ; load 42
DZI #0 ; divide by zero (should error)
STP
value: RST 42
"#;
fs::write(&source_file, program_with_error).unwrap();
let run_output = Command::new("cargo")
.args(&["run", "--", "run", "--extended", source_file.to_str().unwrap()])
.output()
.expect("Failed to execute run command");
assert!(!run_output.status.success(), "Run should fail due to division by zero");
let stderr = String::from_utf8_lossy(&run_output.stderr);
assert!(stderr.contains("DivisionByZero") || stderr.contains("Division by zero"),
"Error message should mention division by zero: {}", stderr);
}
#[test]
fn test_mixed_standard_extended_pipeline() {
let temp_dir = TempDir::new().unwrap();
let source_file = temp_dir.path().join("mixed_test.asmod");
let mixed_program = r#"
; Mixed instruction test: (10 + 5) * 3 / 2 % 7 = 1
start:
POB value1 ; load 10 (standard)
DOD value2 ; add 5 = 15 (standard)
MNO #3 ; multiply by 3 = 45 (extended)
DZI #2 ; divide by 2 = 22 (extended)
MOD #7 ; modulo 7 = 1 (extended)
WYJSCIE ; output result (standard)
STP ; stop (standard)
value1: RST 10
value2: RST 5
"#;
fs::write(&source_file, mixed_program).unwrap();
let run_output = Command::new("cargo")
.args(&["run", "--", "run", "--extended", source_file.to_str().unwrap()])
.output()
.expect("Failed to execute run command");
assert!(run_output.status.success(), "Run failed: {}", String::from_utf8_lossy(&run_output.stderr));
let stdout = String::from_utf8_lossy(&run_output.stdout);
assert!(stdout.contains("1"), "Expected output '1', got: {}", stdout);
}