Phase 6: Kompatibilitaetsabnahme abschliessen und archivieren
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
//! Optionaler öffentlicher Bestand; kein Netzwerk und keine GUI.
|
||||
use std::{path::PathBuf, process::Command, time::Duration};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
time::Duration,
|
||||
};
|
||||
use tb_runtime::host::{CaptureHost, Ereignis, Host};
|
||||
use tb_vm::{
|
||||
bytecode::CompiledModule,
|
||||
@@ -13,14 +17,17 @@ struct PruefHost {
|
||||
inner: CaptureHost,
|
||||
dialogantwort: Option<Ereignis>,
|
||||
dialog_gesehen: bool,
|
||||
dialogtext: Option<&'static str>,
|
||||
}
|
||||
impl Host for PruefHost {
|
||||
fn present(&mut self, screen: &tb_runtime::screen::TextScreen) {
|
||||
self.dialog_gesehen |= tb_runtime::snapshot::text(screen).contains("Save changes to");
|
||||
self.dialog_gesehen |= self
|
||||
.dialogtext
|
||||
.is_some_and(|text| tb_runtime::snapshot::text(screen).contains(text));
|
||||
self.inner.present(screen);
|
||||
}
|
||||
fn next_event(&mut self, block: bool) -> Option<Ereignis> {
|
||||
if block {
|
||||
if self.dialog_gesehen {
|
||||
if let Some(event) = self.dialogantwort.take() {
|
||||
return Some(event);
|
||||
}
|
||||
@@ -37,23 +44,78 @@ impl Host for PruefHost {
|
||||
|
||||
const REVISION: &str = "1cdd2b32b829fe1721d0b6aecc433abc47a96fb6";
|
||||
|
||||
fn pruefe_bestand(repo: Option<&Path>) -> Result<(), String> {
|
||||
let repo = repo.ok_or_else(|| {
|
||||
format!("TB_VBDOS_REPO fehlt: cout/vbdos bei Revision {REVISION} erforderlich")
|
||||
})?;
|
||||
let rev = Command::new("git")
|
||||
.arg("-C")
|
||||
.arg(repo)
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.output()
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Fremdprogrammbestand {}: git nicht ausführbar: {e}",
|
||||
repo.display()
|
||||
)
|
||||
})?;
|
||||
if !rev.status.success() {
|
||||
return Err(format!("Fremdprogrammbestand {} fehlt oder ist kein lesbares Git-Repository; benötigt {REVISION}", repo.display()));
|
||||
}
|
||||
let found = String::from_utf8_lossy(&rev.stdout);
|
||||
if found.trim() != REVISION {
|
||||
return Err(format!(
|
||||
"Fremdprogrammbestand {}: falsche Revision, Soll {REVISION}, Ist {}",
|
||||
repo.display(),
|
||||
found.trim()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fehlender_oder_falscher_fremdbestand_wird_benannt() {
|
||||
assert!(pruefe_bestand(None)
|
||||
.unwrap_err()
|
||||
.contains("TB_VBDOS_REPO fehlt"));
|
||||
let missing = Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
|
||||
assert!(pruefe_bestand(Some(&missing))
|
||||
.unwrap_err()
|
||||
.contains("kein lesbares Git-Repository"));
|
||||
let error = pruefe_bestand(Some(Path::new(env!("CARGO_MANIFEST_DIR")))).unwrap_err();
|
||||
assert!(
|
||||
error.contains("falsche Revision") && error.contains(REVISION),
|
||||
"{error}"
|
||||
);
|
||||
}
|
||||
|
||||
struct TempBestand {
|
||||
original: PathBuf,
|
||||
base: PathBuf,
|
||||
}
|
||||
|
||||
impl Drop for TempBestand {
|
||||
fn drop(&mut self) {
|
||||
let _ = std::env::set_current_dir(&self.original);
|
||||
let _ = std::fs::remove_dir_all(&self.base);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "TB_VBDOS_REPO muss auf cout/vbdos in der dokumentierten Revision zeigen"]
|
||||
fn oeffentliche_formularprogramme_sind_reproduzierbar_bedienbar() {
|
||||
let _frist =
|
||||
prozessfrist::Prozessfrist::neu("öffentlicher VBDOS-Bestand", Duration::from_secs(60));
|
||||
let repo = PathBuf::from(std::env::var_os("TB_VBDOS_REPO").expect("TB_VBDOS_REPO fehlt"));
|
||||
let rev = Command::new("git")
|
||||
.arg("-C")
|
||||
.arg(&repo)
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(rev.status.success());
|
||||
assert_eq!(String::from_utf8_lossy(&rev.stdout).trim(), REVISION);
|
||||
let repo = std::env::var_os("TB_VBDOS_REPO").map(PathBuf::from);
|
||||
pruefe_bestand(repo.as_deref()).unwrap_or_else(|e| panic!("{e}"));
|
||||
let repo = repo.unwrap();
|
||||
let original = std::env::current_dir().unwrap();
|
||||
let base = std::env::temp_dir().join(format!("tb-foreign-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&base).unwrap();
|
||||
let _temp = TempBestand {
|
||||
original: original.clone(),
|
||||
base: base.clone(),
|
||||
};
|
||||
let archive = base.join("source.tar");
|
||||
assert!(Command::new("git")
|
||||
.arg("-C")
|
||||
@@ -78,6 +140,7 @@ fn oeffentliche_formularprogramme_sind_reproduzierbar_bedienbar() {
|
||||
] {
|
||||
let mut results = Vec::new();
|
||||
for repeat in 0..2 {
|
||||
eprintln!("{entry}: Lauf {} kompilieren", repeat + 1);
|
||||
let dir = base.join(format!("run-{repeat}"));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
assert!(Command::new("tar")
|
||||
@@ -105,7 +168,17 @@ fn oeffentliche_formularprogramme_sind_reproduzierbar_bedienbar() {
|
||||
vm.rt.screen.resize(100, 30);
|
||||
vm.forms.resize(100, 30);
|
||||
std::env::set_current_dir(path.parent().unwrap()).unwrap();
|
||||
let mut host = PruefHost::default();
|
||||
let mut host = PruefHost {
|
||||
dialogtext: match entry {
|
||||
"microsoft/check.mak" | "misc/mentors/mentors.frm" => {
|
||||
Some("Save these records?")
|
||||
}
|
||||
"microsoft/notepad.frm" => Some("Save changes to"),
|
||||
_ => None,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
eprintln!("{entry}: Start und initiale Ereignisse");
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended, "{entry}: Start");
|
||||
assert_eq!(
|
||||
vm.run_visible_forms(&mut host),
|
||||
@@ -124,8 +197,9 @@ fn oeffentliche_formularprogramme_sind_reproduzierbar_bedienbar() {
|
||||
let before = tb_runtime::snapshot::snapshot(&vm.rt.screen);
|
||||
let mut states = Vec::new();
|
||||
for (key, shift) in &keys {
|
||||
eprintln!("{entry}: Taste {key:?}, Modifikatoren {shift}");
|
||||
vm.rt.ende = false;
|
||||
if entry == "microsoft/notepad.frm" && *key == "x" {
|
||||
if host.dialogtext.is_some() && *key == "x" {
|
||||
host.dialogantwort = Some(Ereignis::Taste("n".into(), 0));
|
||||
}
|
||||
host.inner.ereignis(Ereignis::Taste((*key).into(), *shift));
|
||||
@@ -167,10 +241,10 @@ fn oeffentliche_formularprogramme_sind_reproduzierbar_bedienbar() {
|
||||
"{entry}: Menü öffnen und Exit auswählen"
|
||||
);
|
||||
}
|
||||
if entry == "microsoft/notepad.frm" {
|
||||
if host.dialogtext.is_some() {
|
||||
assert!(
|
||||
host.dialog_gesehen && host.dialogantwort.is_none(),
|
||||
"Notepad: Save-Dialog mit N beantworten"
|
||||
"{entry}: Save-Dialog mit N beantworten"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
@@ -184,5 +258,9 @@ fn oeffentliche_formularprogramme_sind_reproduzierbar_bedienbar() {
|
||||
}
|
||||
assert_eq!(results[0], results[1], "{entry}: nicht reproduzierbar");
|
||||
}
|
||||
std::fs::remove_dir_all(base).unwrap();
|
||||
std::fs::remove_dir_all(&base).unwrap();
|
||||
assert!(
|
||||
!base.exists(),
|
||||
"temporärer Fremdprogrammbestand blieb zurück"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user