Phase 6: IDE-Exportanbindung abschließen und Change archivieren

This commit is contained in:
2026-09-07 17:00:29 +02:00
parent 25176947ba
commit 69f2002481
31 changed files with 1310 additions and 136 deletions

View File

@@ -283,21 +283,25 @@ fn create(a: &mut App, t: &Temp) -> PathBuf {
path
}
fn exports(a: &mut App, t: &Temp) {
a.export_backend = false;
let stamp = ProjectStamp::capture(&a.project);
for c in [Command::MakeExe, Command::MakeLibrary] {
menu(a, c);
assert!(draw(a).contains("Erzeugen: Phase 6"));
choice(a, 1, "linux");
choice(a, 2, "x86_64");
field(a, 3, "");
let output = if c == Command::MakeLibrary { 1 } else { 3 };
if c == Command::MakeExe {
choice(a, 1, "linux");
choice(a, 2, "x86_64");
}
field(a, output, "");
plain(a, K::Enter);
assert!(a.dialog.as_ref().unwrap().error.contains("Ausgabepfad"));
field(a, 3, &t.0.join("bad.tbc").display().to_string());
field(a, output, &t.0.join("bad.tbc").display().to_string());
plain(a, K::Enter);
assert!(a.dialog.as_ref().unwrap().error.contains("TBC"));
let existing = t.0.join("existing");
let existing = t.0.join("existing.tbl");
fs::write(&existing, "original").unwrap();
field(a, 3, &existing.display().to_string());
field(a, output, &existing.display().to_string());
plain(a, K::Enter);
assert!(a
.dialog
@@ -309,8 +313,8 @@ fn exports(a: &mut App, t: &Temp) {
plain(a, K::Char(' '));
plain(a, K::Enter);
assert_eq!(fs::read_to_string(existing).unwrap(), "original");
let out = t.0.join("native-result");
field(a, 3, &out.display().to_string());
let out = t.0.join("native-result.tbl");
field(a, output, &out.display().to_string());
plain(a, K::Enter);
let request = a.dialog.as_ref().unwrap().request.clone().unwrap();
assert_eq!(request.project, stamp);
@@ -805,3 +809,157 @@ fn reference_matrix_is_complete_and_points_to_runnable_tests() {
}
}
}
fn finish_export(a: &mut App) {
let start = std::time::Instant::now();
while a.dialog.as_ref().unwrap().export_status == ExportStatus::Running {
a.tick_export();
assert!(start.elapsed().as_secs() < 15, "Export hängt");
std::thread::sleep(std::time::Duration::from_millis(2));
}
assert_eq!(
a.dialog.as_ref().unwrap().export_status,
ExportStatus::Success,
"{:?}",
a.dialog
);
plain(a, K::Esc);
}
#[test]
#[ignore = "Echter Zielnachweis: TB_IDE_RUNTIME_DIR muss geprüfte Host-tbrt-Vorlage enthalten"]
fn real_native_ide_exports_and_source_free_cli_library_consumer() {
let runtime_dir =
PathBuf::from(std::env::var_os("TB_IDE_RUNTIME_DIR").expect("TB_IDE_RUNTIME_DIR fehlt"));
let target = tb_export::Target::host().unwrap();
let t = Temp::new();
let mut a = App::new(&t.0, t.0.join("options"), (100, 30)).unwrap();
a.runtime_dir = runtime_dir;
let _project = create(&mut a, &t);
a.project.open_document(&t.0.join("work.bi")).unwrap();
let disk: Vec<_> = a
.project
.documents()
.map(|(_, d)| {
(
d.source_path().to_path_buf(),
fs::read(d.source_path()).unwrap(),
)
})
.collect();
let include = a.project.find_document(&t.0.join("work.bi")).unwrap();
a.project.replace_text(include, 6..7, "7").unwrap();
let stamp = ProjectStamp::capture(&a.project);
// The real project still supports debugging before export.
open_code(&mut a, "Form1.frm");
plain(&mut a, K::F(8));
tick(&mut a, 0);
assert_eq!(a.execution, Execution::Paused, "{}", a.message);
let exe = t.0.join(if cfg!(windows) {
"program.exe"
} else {
"program"
});
menu(&mut a, Command::MakeExe);
let (system, arch) = tb_ide::export::target_parts(target);
choice(&mut a, 1, system);
choice(&mut a, 2, arch);
field(&mut a, 3, &exe.display().to_string());
plain(&mut a, K::Enter);
finish_export(&mut a);
assert_eq!(
tb_export::native_target(&fs::read(&exe).unwrap()).unwrap(),
target
);
let library = t.0.join("library.tbl");
menu(&mut a, Command::MakeLibrary);
field(&mut a, 1, &library.display().to_string());
click_hit(&mut a, |h| matches!(h, Hit::DialogSubmit));
finish_export(&mut a);
let mut compiler = tb_vm::project::ProjectCompiler::default();
compiler.debug_symbols = true;
let expected = a
.project
.sources()
.unwrap()
.compile_library(&mut compiler)
.unwrap()
.to_tbl()
.unwrap();
assert_eq!(fs::read(&library).unwrap(), expected);
assert_eq!(ProjectStamp::capture(&a.project), stamp);
assert!(a.project.document(include).unwrap().is_dirty());
for (path, bytes) in disk {
assert_eq!(fs::read(&path).unwrap(), bytes);
fs::remove_file(path).unwrap();
}
fs::remove_file(t.0.join("project.mak")).unwrap();
let consumer = t.0.join("consumer.bas");
fs::write(
&consumer,
"DIM n AS INTEGER\nn=8\nCALL Bump(n)\nPRINT n\nEND\n",
)
.unwrap();
let tbc = t.0.join("consumer.tbc");
// Exactly the tbc link command implementation, with the library sources gone.
build(
&[
consumer.display().to_string(),
library.display().to_string(),
"-o".into(),
tbc.display().to_string(),
],
true,
)
.unwrap();
let cli = tb_vm::project_io::load_program(&tbc).unwrap();
fs::write(t.0.join("consumer.mak"), "consumer.bas\nlibrary.tbl\n").unwrap();
let mut other = App::new(&t.0, t.0.join("other-options"), (100, 30)).unwrap();
other
.load_initial_project(t.0.join("consumer.mak"))
.unwrap();
other.start_execution().unwrap();
tick(&mut other, 0);
assert_eq!(snapshot::text(other.session.screen()), " 9 \n");
let mut cli_vm = new_execution(cli, "", None, None).unwrap();
let mut host = tb_runtime::host::CaptureHost::default();
assert!(matches!(cli_vm.run(&mut host), RunEvent::Ended));
assert_eq!(
snapshot::text(&cli_vm.rt.screen),
snapshot::text(other.session.screen())
);
fs::remove_file(&library).unwrap();
fs::remove_file(&tbc).unwrap();
fs::remove_file(&consumer).unwrap();
use std::io::Write;
let mut child = std::process::Command::new(&exe)
.arg("!")
.current_dir(&t.0)
.env("PATH", "")
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap();
child.stdin.take().unwrap().write_all(b"abc\n").unwrap();
let start = std::time::Instant::now();
while child.try_wait().unwrap().is_none() {
if start.elapsed().as_secs() > 10 {
let _ = child.kill();
panic!("Natives IDE-Programm hängt");
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
let result = child.wait_with_output().unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
let output = String::from_utf8_lossy(&result.stdout).replace("\r\n", "\n");
assert!(output.contains("8 abc!"), "{output:?}");
assert!(fs::read_to_string(t.0.join("result.txt"))
.unwrap()
.contains('8'));
}