main.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /*
  2. * This is a small program that is meant for testing the AML parser on artificial
  3. * AML. We want to:
  4. * - scan a directory for ASL files
  5. * - compile them using `iasl` into AML files (these should be gitignored), but only if the ASL file has a
  6. * newer timestamp than the AML file (or just compile if there isn't a corresponding AML file)
  7. * - Run the AML parser on each AML file, printing test output like `cargo test` does in a nice table for
  8. * each AML file
  9. * - For failing tests, print out a nice summary of the errors for each file
  10. */
  11. use aml::{AmlContext, DebugVerbosity};
  12. use clap::{App, Arg};
  13. use std::{
  14. ffi::OsStr,
  15. fs::{self, File},
  16. io::{Read, Write},
  17. ops::Not,
  18. path::Path,
  19. process::Command,
  20. };
  21. fn main() -> std::io::Result<()> {
  22. log::set_logger(&Logger).unwrap();
  23. log::set_max_level(log::LevelFilter::Trace);
  24. let matches = App::new("aml_tester")
  25. .version("v0.1.0")
  26. .author("Isaac Woods")
  27. .about("Compiles and tests ASL files")
  28. .arg(Arg::with_name("path").short("p").long("path").required(true).takes_value(true))
  29. .arg(Arg::with_name("no_compile").long("no-compile"))
  30. .get_matches();
  31. let dir_path = Path::new(matches.value_of("path").unwrap());
  32. println!("Running tests in directory: {:?}", dir_path);
  33. if !matches.is_present("no_compile") {
  34. compile_asl_files(dir_path)?;
  35. }
  36. /*
  37. * Now, we find all the AML files in the directory, and try to compile them with the `aml`
  38. * parser.
  39. */
  40. let aml_files = fs::read_dir(dir_path)?
  41. .filter(|entry| entry.is_ok() && entry.as_ref().unwrap().path().extension() == Some(OsStr::new("aml")))
  42. .map(|entry| entry.unwrap());
  43. let (passed, failed) = aml_files.fold((0, 0), |(passed, failed), file_entry| {
  44. print!("Testing AML file: {:?}... ", file_entry.path());
  45. std::io::stdout().flush().unwrap();
  46. let mut file = File::open(file_entry.path()).unwrap();
  47. let mut contents = Vec::new();
  48. file.read_to_end(&mut contents).unwrap();
  49. const AML_TABLE_HEADER_LENGTH: usize = 36;
  50. let mut context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
  51. match context.parse_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
  52. Ok(()) => {
  53. println!("{}OK{}", termion::color::Fg(termion::color::Green), termion::style::Reset);
  54. println!("Namespace: {:#?}", context.namespace);
  55. (passed + 1, failed)
  56. }
  57. Err(err) => {
  58. println!("{}Failed ({:?}){}", termion::color::Fg(termion::color::Red), err, termion::style::Reset);
  59. println!("Namespace: {:#?}", context.namespace);
  60. (passed, failed + 1)
  61. }
  62. }
  63. });
  64. println!("Test results: {} passed, {} failed", passed, failed);
  65. Ok(())
  66. }
  67. fn compile_asl_files(dir_path: &Path) -> std::io::Result<()> {
  68. let mut asl_files = fs::read_dir(dir_path)?
  69. .filter(|entry| entry.is_ok() && entry.as_ref().unwrap().path().extension() == Some(OsStr::new("asl")))
  70. .map(|file| file.unwrap())
  71. .peekable();
  72. if !asl_files.peek().is_none() {
  73. // Test if `iasl` is installed, so we can give a good error if it's not
  74. if Command::new("iasl").arg("-v").status().unwrap().success().not() {
  75. panic!("`iasl` is not installed, but we want to compile some ASL files! Pass --no-compile, or install `iasl`");
  76. }
  77. }
  78. for file in asl_files {
  79. let aml_path = file.path().with_extension(OsStr::new("aml"));
  80. /*
  81. * Check if an AML path with a matching last-modified date exists. If it
  82. * does, we don't need to compile the ASL file again.
  83. */
  84. if aml_path.is_file() {
  85. let asl_last_modified = file.metadata()?.modified()?;
  86. let aml_last_modified = aml_path.metadata()?.modified()?;
  87. if asl_last_modified <= aml_last_modified {
  88. continue;
  89. }
  90. }
  91. // Compile the ASL file using `iasl`
  92. println!("Compiling file: {}", file.path().to_str().unwrap());
  93. let output = Command::new("iasl").arg(file.path()).output()?;
  94. if !output.status.success() {
  95. // TODO: this doesn't print the whole output of `iasl` for some reason (no actual error messages), but
  96. // it doesn't seem to be on stdout either. No idea how it ends up at the shell tbh; would be good to
  97. // find it and print it here.
  98. println!(
  99. "Failed to compile ASL file: {}. Output from iasl: {}",
  100. file.path().to_str().unwrap(),
  101. String::from_utf8_lossy(&output.stderr)
  102. );
  103. }
  104. }
  105. Ok(())
  106. }
  107. struct Logger;
  108. impl log::Log for Logger {
  109. fn enabled(&self, _: &log::Metadata) -> bool {
  110. true
  111. }
  112. fn log(&self, record: &log::Record) {
  113. println!("[{}] {}", record.level(), record.args());
  114. }
  115. fn flush(&self) {
  116. std::io::stdout().flush().unwrap();
  117. }
  118. }
  119. struct Handler;
  120. impl aml::Handler for Handler {
  121. fn read_u8(&self, _address: usize) -> u8 {
  122. unimplemented!()
  123. }
  124. fn read_u16(&self, _address: usize) -> u16 {
  125. unimplemented!()
  126. }
  127. fn read_u32(&self, _address: usize) -> u32 {
  128. unimplemented!()
  129. }
  130. fn read_u64(&self, _address: usize) -> u64 {
  131. unimplemented!()
  132. }
  133. fn write_u8(&mut self, _address: usize, _value: u8) {
  134. unimplemented!()
  135. }
  136. fn write_u16(&mut self, _address: usize, _value: u16) {
  137. unimplemented!()
  138. }
  139. fn write_u32(&mut self, _address: usize, _value: u32) {
  140. unimplemented!()
  141. }
  142. fn write_u64(&mut self, _address: usize, _value: u64) {
  143. unimplemented!()
  144. }
  145. fn read_io_u8(&self, _port: u16) -> u8 {
  146. unimplemented!()
  147. }
  148. fn read_io_u16(&self, _port: u16) -> u16 {
  149. unimplemented!()
  150. }
  151. fn read_io_u32(&self, _port: u16) -> u32 {
  152. unimplemented!()
  153. }
  154. fn write_io_u8(&self, _port: u16, _value: u8) {
  155. unimplemented!()
  156. }
  157. fn write_io_u16(&self, _port: u16, _value: u16) {
  158. unimplemented!()
  159. }
  160. fn write_io_u32(&self, _port: u16, _value: u32) {
  161. unimplemented!()
  162. }
  163. fn read_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u8 {
  164. unimplemented!()
  165. }
  166. fn read_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u16 {
  167. unimplemented!()
  168. }
  169. fn read_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u32 {
  170. unimplemented!()
  171. }
  172. fn write_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u8) {
  173. unimplemented!()
  174. }
  175. fn write_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u16) {
  176. unimplemented!()
  177. }
  178. fn write_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u32) {
  179. unimplemented!()
  180. }
  181. }