From b9f193b721f11e1e7cfa0841916a8503391015d3 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 8 Jun 2025 09:31:18 -0700 Subject: [PATCH] work on system file tests --- rust/pspp/src/dictionary.rs | 8 + rust/pspp/src/output/mod.rs | 66 +- rust/pspp/src/output/pivot/test.rs | 39 +- rust/pspp/src/output/spv.rs | 2 +- rust/pspp/src/output/text.rs | 77 ++- rust/pspp/src/sys/sack.rs | 21 +- rust/pspp/src/sys/test.rs | 628 ++---------------- rust/pspp/src/sys/testdata/documents.expected | 24 + rust/pspp/src/sys/testdata/documents.sack | 34 + ...fied_number_of_variable_positions.expected | 20 + ...pecified_number_of_variable_positions.sack | 26 + .../src/sys/testdata/value_labels.expected | 93 +++ rust/pspp/src/sys/testdata/value_labels.sack | 109 +++ ...ariable_labels_and_missing_values.expected | 40 ++ .../variable_labels_and_missing_values.sack | 117 ++++ .../wrong_variable_positions_but_v13.expected | 21 + .../wrong_variable_positions_but_v13.sack | 29 + rust/pspp/tests/sack.rs | 2 +- 18 files changed, 730 insertions(+), 626 deletions(-) create mode 100644 rust/pspp/src/sys/testdata/documents.expected create mode 100644 rust/pspp/src/sys/testdata/documents.sack create mode 100644 rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.expected create mode 100644 rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.sack create mode 100644 rust/pspp/src/sys/testdata/value_labels.expected create mode 100644 rust/pspp/src/sys/testdata/value_labels.sack create mode 100644 rust/pspp/src/sys/testdata/variable_labels_and_missing_values.expected create mode 100644 rust/pspp/src/sys/testdata/variable_labels_and_missing_values.sack create mode 100644 rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.expected create mode 100644 rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.sack diff --git a/rust/pspp/src/dictionary.rs b/rust/pspp/src/dictionary.rs index 0a759ec1ff..38a22b1ae5 100644 --- a/rust/pspp/src/dictionary.rs +++ b/rust/pspp/src/dictionary.rs @@ -570,6 +570,14 @@ impl Dictionary { None => values.push(Value::empty()), } + group.push("Documents"); + values.push(Value::new_user_text( + self.documents + .iter() + .flat_map(|s| [s.as_str(), "\n"]) + .collect::(), + )); + (group, values) } } diff --git a/rust/pspp/src/output/mod.rs b/rust/pspp/src/output/mod.rs index 4ff8d3bea4..ff054b9e46 100644 --- a/rust/pspp/src/output/mod.rs +++ b/rust/pspp/src/output/mod.rs @@ -44,12 +44,13 @@ pub struct Item { } impl Item { - pub fn new(details: Details) -> Self { + pub fn new(details: impl Into
) -> Self { + let details = details.into(); Self { label: None, command_name: details.command_name().cloned(), show: true, - details, + details: details, } } @@ -61,11 +62,20 @@ impl Item { } } +impl From for Item +where + T: Into
, +{ + fn from(value: T) -> Self { + Self::new(value) + } +} + pub enum Details { Chart, Image, Group(Vec>), - Message(Diagnostic), + Message(Box), PageBreak, Table(Box), Text(Box), @@ -111,21 +121,63 @@ impl Details { } } +impl From for Details { + fn from(value: Diagnostic) -> Self { + Self::Message(Box::new(value)) + } +} + +impl From> for Details { + fn from(value: Box) -> Self { + Self::Message(value) + } +} + +impl From for Details { + fn from(value: PivotTable) -> Self { + Self::Table(Box::new(value)) + } +} + +impl From> for Details { + fn from(value: Box) -> Self { + Self::Table(value) + } +} + +impl From for Details { + fn from(value: Text) -> Self { + Self::Text(Box::new(value)) + } +} + +impl From> for Details { + fn from(value: Box) -> Self { + Self::Text(value) + } +} + pub struct Text { type_: TextType, content: Value, } -impl From<&Diagnostic> for Text { - fn from(value: &Diagnostic) -> Self { - Text { +impl Text { + pub fn new_log(s: impl Into) -> Self { + Self { type_: TextType::Log, - content: Value::new_user_text(value.to_string()), + content: Value::new_user_text(s), } } } +impl From<&Diagnostic> for Text { + fn from(value: &Diagnostic) -> Self { + Self::new_log(value.to_string()) + } +} + pub enum TextType { /// `TITLE` and `SUBTITLE` commands. PageTitle, diff --git a/rust/pspp/src/output/pivot/test.rs b/rust/pspp/src/output/pivot/test.rs index a867cdd80e..9c55855e86 100644 --- a/rust/pspp/src/output/pivot/test.rs +++ b/rust/pspp/src/output/pivot/test.rs @@ -1,4 +1,4 @@ -use std::{fs::File, path::Path, sync::Arc}; +use std::{fmt::Display, fs::File, path::Path, sync::Arc}; use enum_map::EnumMap; @@ -123,19 +123,38 @@ fn d2(title: &str, axes: [Axis3; 2], dimension_labels: Option) -> } #[track_caller] -pub fn assert_rendering(name: &str, pivot_table: &PivotTable, expected: &str) { - let actual = pivot_table.to_string(); - if actual != expected { - eprintln!("Unexpected pivot table rendering:\n--- expected\n+++ actual"); +pub fn assert_lines_eq(expected: &str, expected_name: E, actual: &str, actual_name: A) +where + E: Display, + A: Display, +{ + if expected != actual { + eprintln!("Unexpected output:\n--- {expected_name}\n+++ {actual_name}"); for result in diff::lines(expected, &actual) { - match result { - diff::Result::Left(line) => eprintln!("-{line:?}"), - diff::Result::Both(line, _) => eprintln!(" {line:?}"), - diff::Result::Right(line) => eprintln!("+{line:?}"), - } + let (prefix, line) = match result { + diff::Result::Left(line) => ('-', line), + diff::Result::Both(line, _) => (' ', line), + diff::Result::Right(line) => ('+', line), + }; + let suffix = if line.trim_end().len() != line.len() { + "$" + } else { + "" + }; + eprintln!("{prefix}{line}{suffix}"); } panic!(); } +} + +#[track_caller] +pub fn assert_rendering(name: &str, pivot_table: &PivotTable, expected: &str) { + assert_lines_eq( + expected, + format!("{name} expected"), + &pivot_table.to_string(), + format!("{name} actual"), + ); let item = Arc::new(Item::new(Details::Table(Box::new(pivot_table.clone())))); if let Some(dir) = std::env::var_os("PSPP_TEST_HTML_DIR") { diff --git a/rust/pspp/src/output/spv.rs b/rust/pspp/src/output/spv.rs index 9635655355..96345c93a2 100644 --- a/rust/pspp/src/output/spv.rs +++ b/rust/pspp/src/output/spv.rs @@ -166,7 +166,7 @@ where .unwrap(); } super::Details::Message(diagnostic) => { - self.write_text(item, &diagnostic.into(), structure) + self.write_text(item, &Text::from(diagnostic.as_ref()), structure) } super::Details::PageBreak => { self.needs_page_break = true; diff --git a/rust/pspp/src/output/text.rs b/rust/pspp/src/output/text.rs index 538d80d50a..c29fad0545 100644 --- a/rust/pspp/src/output/text.rs +++ b/rust/pspp/src/output/text.rs @@ -1,8 +1,8 @@ use std::{ borrow::Cow, - fmt::Display, + fmt::{Display, Error as FmtError, Result as FmtResult, Write as FmtWrite}, fs::File, - io::{BufWriter, Write}, + io::{BufWriter, Write as IoWrite}, ops::{Index, Range}, sync::{Arc, LazyLock}, }; @@ -326,10 +326,13 @@ impl<'a> DisplayPivotTable<'a> { impl Display for DisplayPivotTable<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for line in TextRenderer::default().render(self.pt) { - writeln!(f, "{}", line)?; - } - Ok(()) + TextRenderer::default().render_table(self.pt, f) + } +} + +impl Display for Item { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + TextRenderer::default().render(self, f) } } @@ -348,20 +351,47 @@ impl TextDriver { } impl TextRenderer { - fn render(&mut self, table: &PivotTable) -> Vec { - let mut output = Vec::new(); + fn render(&mut self, item: &Item, writer: &mut W) -> FmtResult + where + W: FmtWrite, + { + match &item.details { + Details::Chart => todo!(), + Details::Image => todo!(), + Details::Group(children) => { + for (index, child) in children.iter().enumerate() { + if index > 0 { + writeln!(writer)?; + } + self.render(child, writer)?; + } + Ok(()) + } + Details::Message(_diagnostic) => todo!(), + Details::PageBreak => Ok(()), + Details::Table(pivot_table) => self.render_table(&*pivot_table, writer), + Details::Text(_text) => todo!(), + } + } + + fn render_table(&mut self, table: &PivotTable, writer: &mut W) -> FmtResult + where + W: FmtWrite, + { for (index, layer_indexes) in table.layers(true).enumerate() { if index > 0 { - output.push(TextLine::new()); + writeln!(writer)?; } let mut pager = Pager::new(self, table, Some(layer_indexes.as_slice())); while pager.has_next(self) { pager.draw_next(self, usize::MAX); - output.append(&mut self.lines); + for line in self.lines.drain(..) { + writeln!(writer, "{}", line)?; + } } } - output + Ok(()) } fn layout_cell(&self, text: &str, bb: Rect2) -> Coord2 { @@ -472,19 +502,18 @@ impl Driver for TextDriver { } fn write(&mut self, item: &Arc) { - match &item.details { - Details::Chart => todo!(), - Details::Image => todo!(), - Details::Group(_) => todo!(), - Details::Message(_diagnostic) => todo!(), - Details::PageBreak => (), - Details::Table(pivot_table) => { - for line in self.renderer.render(pivot_table) { - writeln!(self.file, "{}", line.str()).unwrap(); - } - } - Details::Text(_text) => todo!(), - } + let _ = self.renderer.render(item, &mut FmtAdapter(&mut self.file)); + } +} + +struct FmtAdapter(W); + +impl FmtWrite for FmtAdapter +where + W: IoWrite, +{ + fn write_str(&mut self, s: &str) -> FmtResult { + self.0.write_all(s.as_bytes()).map_err(|_| FmtError) } } diff --git a/rust/pspp/src/sys/sack.rs b/rust/pspp/src/sys/sack.rs index b2ac013beb..4620c4c9a8 100644 --- a/rust/pspp/src/sys/sack.rs +++ b/rust/pspp/src/sys/sack.rs @@ -6,6 +6,7 @@ use std::{ error::Error as StdError, fmt::{Display, Formatter, Result as FmtResult}, iter::repeat_n, + path::{Path, PathBuf}, }; use crate::endian::{Endian, ToBytes}; @@ -14,7 +15,7 @@ pub type Result = std::result::Result; #[derive(Debug)] pub struct Error { - pub file_name: Option, + pub file_name: Option, pub line_number: Option, pub token: Option, pub message: String, @@ -22,13 +23,13 @@ pub struct Error { impl Error { fn new( - file_name: Option<&str>, + file_name: Option<&Path>, line_number: Option, token: Option<&str>, message: String, ) -> Error { Error { - file_name: file_name.map(String::from), + file_name: file_name.map(PathBuf::from), line_number, token: token.map(String::from), message, @@ -41,8 +42,10 @@ impl StdError for Error {} impl Display for Error { fn fmt(&self, f: &mut Formatter) -> FmtResult { match (self.file_name.as_ref(), self.line_number) { - (Some(ref file_name), Some(line_number)) => write!(f, "{file_name}:{line_number}: ")?, - (Some(ref file_name), None) => write!(f, "{file_name}: ")?, + (Some(ref file_name), Some(line_number)) => { + write!(f, "{}:{line_number}: ", file_name.display())? + } + (Some(ref file_name), None) => write!(f, "{}: ", file_name.display())?, (None, Some(line_number)) => write!(f, "line {line_number}: ")?, (None, None) => (), } @@ -53,7 +56,7 @@ impl Display for Error { } } -pub fn sack(input: &str, input_file_name: Option<&str>, endian: Endian) -> Result> { +pub fn sack(input: &str, input_file_name: Option<&Path>, endian: Endian) -> Result> { let mut symbol_table = HashMap::new(); let output = _sack(input, input_file_name, endian, &mut symbol_table)?; let output = if !symbol_table.is_empty() { @@ -76,7 +79,7 @@ pub fn sack(input: &str, input_file_name: Option<&str>, endian: Endian) -> Resul fn _sack( input: &str, - input_file_name: Option<&str>, + input_file_name: Option<&Path>, endian: Endian, symbol_table: &mut HashMap>, ) -> Result> { @@ -337,7 +340,7 @@ enum Token { struct Lexer<'a> { input: &'a str, token: Option<(Token, &'a str)>, - input_file_name: Option<&'a str>, + input_file_name: Option<&'a Path>, line_number: usize, endian: Endian, } @@ -363,7 +366,7 @@ fn skip_comments(mut s: &str) -> (&str, usize) { } impl<'a> Lexer<'a> { - fn new(input: &'a str, input_file_name: Option<&'a str>, endian: Endian) -> Result> { + fn new(input: &'a str, input_file_name: Option<&'a Path>, endian: Endian) -> Result> { let mut lexer = Lexer { input, token: None, diff --git a/rust/pspp/src/sys/test.rs b/rust/pspp/src/sys/test.rs index 95f380b9b4..ce107c1cc3 100644 --- a/rust/pspp/src/sys/test.rs +++ b/rust/pspp/src/sys/test.rs @@ -1,8 +1,14 @@ -use std::io::Cursor; +use std::{io::Cursor, path::Path, sync::Arc}; use crate::{ endian::Endian, - output::pivot::{test::assert_rendering, Axis3, Dimension, PivotTable}, + output::{ + pivot::{ + test::{assert_lines_eq, assert_rendering}, + Axis3, Dimension, PivotTable, + }, + Details, Item, Text, + }, sys::{ cooked::{decode, Headers, Metadata}, raw::{encoding_from_headers, Decoder, Reader, Record}, @@ -15,306 +21,39 @@ use enum_iterator::all; #[test] fn variable_labels_and_missing_values() { - for endian in all::() { - let input = r#" -# File header. -"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; -2; # Layout code -28; # Nominal case size -0; # Not compressed -0; # Not weighted -1; # 1 case. -100.0; # Bias. -"05 Jan 11"; "20:53:52"; -"PSPP synthetic test file: "; i8 244; i8 245; i8 246; i8 248; s34 ""; -i8 0 *3; - -# Numeric variable, no label or missing values. -2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; - -# Numeric variable, variable label. -2; 0; 1; 0; 0x050800 *2; s8 "NUM2"; -32; "Numeric variable 2's label ("; i8 249; i8 250; i8 251; ")"; - -# Numeric variable, one missing value. -2; 0; 0; 1; 0x050800 *2; s8 "NUM3"; -1.0; - -# Numeric variable, variable label and missing value. -2; 0; 1; 1; 0x050800 *2; s8 "NUM4"; -30; "Another numeric variable label"; i8 0 * 2; -1.0; - -# Numeric variable, two missing values. -2; 0; 0; 2; 0x050800 *2; s8 "NUM5"; 1.0; 2.0; - -# Numeric variable, three missing values. -2; 0; 0; 3; 0x050800 *2; s8 "NUM6"; 1.0; 2.0; 3.0; - -# Numeric variable, range of missing values. -2; 0; 0; -2; 0x050800 *2; s8 "NUM7"; 1.0; 3.0; - -# Numeric variables, range of missing values plus discrete value. -2; 0; 0; -3; 0x050800 *2; s8 "NUM8"; 1.0; 3.0; 5.0; -2; 0; 0; -3; 0x050800 *2; s8 "NUM9"; 1.0; HIGHEST; -5.0; -2; 0; 0; -3; 0x050800 *2; "NUM"; i8 192; i8 200; i8 204; i8 209; i8 210; -LOWEST; 1.0; 5.0; - -# String variable, no label or missing values. -2; 4; 0; 0; 0x010400 *2; s8 "STR1"; - -# String variable, variable label. -2; 4; 1; 0; 0x010400 *2; s8 "STR2"; -25; "String variable 2's label"; i8 0 * 3; - -# String variable, one missing value. -2; 4; 0; 1; 0x010400 *2; s8 "STR3"; s8 "MISS"; - -# String variable, variable label and missing value. -2; 4; 1; 1; 0x010400 *2; s8 "STR4"; -29; "Another string variable label"; i8 0 * 3; -s8 "OTHR"; - -# String variable, two missing values. -2; 4; 0; 2; 0x010400 *2; s8 "STR5"; s8 "MISS"; s8 "OTHR"; - -# String variable, three missing values. -2; 4; 0; 3; 0x010400 *2; s8 "STR6"; s8 "MISS"; s8 "OTHR"; s8 "MORE"; - -# Long string variable, one missing value. -# (This is not how SPSS represents missing values for long strings--it -# uses a separate record as shown later below--but old versions of PSPP -# did use this representation so we continue supporting it for backward -# compatibility. -2; 11; 0; 1; 0x010b00 *2; s8 "STR7"; "first8by"; -2; -1; 0; 0; 0; 0; s8 ""; - -# Long string variables that will have missing values added with a -# later record. -2; 9; 0; 0; 0x010900 *2; s8 "STR8"; -2; -1; 0; 0; 0; 0; s8 ""; -2; 10; 0; 0; 0x010a00 *2; s8 "STR9"; -2; -1; 0; 0; 0; 0; s8 ""; -2; 11; 0; 0; 0x010b00 *2; s8 "STR10"; -2; -1; 0; 0; 0; 0; s8 ""; - -# Long string variable, value label. -2; 25; 1; 0; 0x011900 *2; s8 "STR11"; 14; "25-byte string"; i8 0 * 2; -( 2; -1; 0; 0; 0; 0; s8 ""; ) * 2; -# Variable label fields on continuation records have been spotted in system -# files created by "SPSS Power Macintosh Release 6.1". -2; -1; 1; 0; 0; 0; s8 ""; 20; "dummy variable label"; - -# Machine integer info record. -7; 3; 4; 8; 1; 2; 3; -1; 1; 1; ENDIAN; 1252; - -# Machine floating-point info record. -7; 4; 8; 3; SYSMIS; HIGHEST; LOWEST; - -# Long string variable missing values record. -7; 22; 1; COUNT ( -# One missing value for STR8. -COUNT("STR8"); i8 1; 8; "abcdefgh"; - -# Two missing values for STR9. -COUNT("STR9"); i8 2; 8; "abcdefgh"; "01234567"; - -# Three missing values for STR9. -COUNT("STR10"); i8 3; 8; "abcdefgh"; "01234567"; "0 "; -); - -# Character encoding record. -7; 20; 1; 12; "windows-1252"; - -# Dictionary termination record. -999; 0; - -# Data. -1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 9.0; 10.0; -s8 "abcd"; s8 "efgh"; s8 "ijkl"; s8 "mnop"; s8 "qrst"; s8 "uvwx"; -s16 "yzABCDEFGHI"; s16 "JKLMNOPQR"; s16 "STUVWXYZ01"; -s16 "23456789abc"; s32 "defghijklmnopqstuvwxyzABC"; -"#; - let sysfile = sack(input, None, endian).unwrap(); - let cursor = Cursor::new(sysfile); - let reader = Reader::new(cursor, |warning| println!("{warning}")).unwrap(); - let headers: Vec = reader.collect::, _>>().unwrap(); - let encoding = encoding_from_headers(&headers, &|e| eprintln!("{e}")).unwrap(); - let decoder = Decoder::new(encoding, |e| eprintln!("{e}")); - let mut decoded_records = Vec::new(); - for header in headers { - decoded_records.push(header.decode(&decoder).unwrap()); - } - - let mut errors = Vec::new(); - let headers = Headers::new(decoded_records, &mut |e| errors.push(e)).unwrap(); - let (dictionary, metadata) = decode(headers, encoding, |e| errors.push(e)).unwrap(); - assert_eq!(errors, vec![]); - assert_eq!( - metadata, - Metadata { - product: "$(#) SPSS DATA FILE PSPP synthetic test file".into(), - ..test_metadata(endian) - } - ); - assert_eq!( - dictionary.file_label.as_ref().map(|s| s.as_str()), - Some("PSPP synthetic test file: ôõöø") - ); - assert!(dictionary.output_value_labels().to_pivot_table().is_none()); - let pt = dictionary.output_variables().to_pivot_table(); - assert_rendering( - "variable_labels_and_missing_values", - &pt, - r#"╭────────────────────────────────┬────────┬────────────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────────────╮ -│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│ Missing Values │ -├────────────────────────────────┼────────┼────────────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────────────┤ -│num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ -│Numeric variable 2's label (ùúû)│ 2│Numeric variable 2's label (ùúû)│ │Input│ 8│Right │F8.0 │F8.0 │ │ -│num3 │ 3│ │ │Input│ 8│Right │F8.0 │F8.0 │1 │ -│Another numeric variable label │ 4│Another numeric variable label │ │Input│ 8│Right │F8.0 │F8.0 │1 │ -│num5 │ 5│ │ │Input│ 8│Right │F8.0 │F8.0 │1; 2 │ -│num6 │ 6│ │ │Input│ 8│Right │F8.0 │F8.0 │1; 2; 3 │ -│num7 │ 7│ │ │Input│ 8│Right │F8.0 │F8.0 │1 THRU 3 │ -│num8 │ 8│ │ │Input│ 8│Right │F8.0 │F8.0 │1 THRU 3; 5 │ -│num9 │ 9│ │ │Input│ 8│Right │F8.0 │F8.0 │1 THRU HIGH; -5 │ -│numàèìñò │ 10│ │ │Input│ 8│Right │F8.0 │F8.0 │LOW THRU 1; 5 │ -│str1 │ 11│ │Nominal │Input│ 4│Left │A4 │A4 │ │ -│String variable 2's label │ 12│String variable 2's label │Nominal │Input│ 4│Left │A4 │A4 │ │ -│str3 │ 13│ │Nominal │Input│ 4│Left │A4 │A4 │"MISS" │ -│Another string variable label │ 14│Another string variable label │Nominal │Input│ 4│Left │A4 │A4 │"OTHR" │ -│str5 │ 15│ │Nominal │Input│ 4│Left │A4 │A4 │"MISS"; "OTHR" │ -│str6 │ 16│ │Nominal │Input│ 4│Left │A4 │A4 │"MISS"; "OTHR"; "MORE"│ -│str7 │ 17│ │Nominal │Input│ 11│Left │A11 │A11 │"first8by" │ -│str8 │ 18│ │Nominal │Input│ 9│Left │A9 │A9 │ │ -│str9 │ 19│ │Nominal │Input│ 10│Left │A10 │A10 │ │ -│str10 │ 20│ │Nominal │Input│ 11│Left │A11 │A11 │ │ -│25-byte string │ 21│25-byte string │Nominal │Input│ 25│Left │A25 │A25 │ │ -╰────────────────────────────────┴────────┴────────────────────────────────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────────────╯ -"#, - ); - } -} - -fn test_metadata(endian: Endian) -> Metadata { - Metadata { - creation: NaiveDate::from_ymd_opt(2011, 1, 5) - .unwrap() - .and_time(NaiveTime::from_hms_opt(20, 53, 52).unwrap()), - endian, - compression: None, - n_cases: Some(1), - product: "$(#) SPSS DATA FILE PSPP synthetic test file".into(), - product_ext: None, - version: Some((1, 2, 3)), - } + test_sysfile("variable_labels_and_missing_values"); } #[test] fn unspecified_number_of_variable_positions() { - for endian in all::() { - let input = r#" -# File header. -"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; -2; # Layout code --1; # Nominal case size (unspecified) -0; # Not compressed -0; # Not weighted -1; # 1 case. -100.0; # Bias. -"05 Jan 11"; "20:53:52"; s64 "PSPP synthetic test file"; -i8 0 *3; - -# Numeric variable, no label or missing values. -2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; - -# Numeric variable, variable label. -2; 0; 1; 0; 0x050800 *2; s8 "NUM2"; -26; "Numeric variable 2's label"; i8 0 *2; - -# Character encoding record. -7; 20; 1; 12; "windows-1252"; - -# Dictionary termination record. -999; 0; - -# Data. -1.0; 2.0; -"#; - let sysfile = sack(input, None, endian).unwrap(); - let cursor = Cursor::new(sysfile); - let reader = Reader::new(cursor, |warning| println!("{warning}")).unwrap(); - let headers: Vec = reader.collect::, _>>().unwrap(); - let encoding = encoding_from_headers(&headers, &|e| eprintln!("{e}")).unwrap(); - let decoder = Decoder::new(encoding, |e| eprintln!("{e}")); - let mut decoded_records = Vec::new(); - for header in headers { - decoded_records.push(header.decode(&decoder).unwrap()); - } - - let mut errors = Vec::new(); - let headers = Headers::new(decoded_records, &mut |e| errors.push(e)).unwrap(); - let (dictionary, metadata) = decode(headers, encoding, |e| errors.push(e)).unwrap(); - assert_eq!(errors, vec![]); - assert_eq!( - metadata, - Metadata { - version: None, - ..test_metadata(endian) - } - ); - assert_eq!( - dictionary.file_label.as_ref().map(|s| s.as_str()), - Some("PSPP synthetic test file") - ); - assert!(dictionary.output_value_labels().to_pivot_table().is_none()); - let pt = dictionary.output_variables().to_pivot_table(); - assert_rendering("value_labels_dictionary", &pt, "\ -╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮ -│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│ -├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤ -│num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ -│Numeric variable 2's label│ 2│Numeric variable 2's label│ │Input│ 8│Right │F8.0 │F8.0 │ │ -╰──────────────────────────┴────────┴──────────────────────────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯ -"); - } + test_sysfile("unspecified_number_of_variable_positions"); } #[test] fn wrong_variable_positions_but_v13() { - for endian in all::() { - let input = r#" -# File header. -"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; -2; # Layout code --1; # Nominal case size (unspecified) -0; # Not compressed -0; # Not weighted -1; # 1 case. -100.0; # Bias. -"05 Jan 11"; "20:53:52"; s64 "PSPP synthetic test file"; -i8 0 *3; - -# Numeric variable, no label or missing values. -2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; - -# Numeric variable, variable label. -2; 0; 1; 0; 0x050800 *2; s8 "NUM2"; -26; "Numeric variable 2's label"; i8 0 *2; - -# Machine integer info record (SPSS 13). -7; 3; 4; 8; 13; 2; 3; -1; 1; 1; ENDIAN; 1252; + test_sysfile("wrong_variable_positions_but_v13"); +} -# Character encoding record. -7; 20; 1; 12; "windows-1252"; +#[test] +fn value_labels() { + test_sysfile("value_labels"); +} -# Dictionary termination record. -999; 0; +#[test] +fn documents() { + test_sysfile("documents"); +} -# Data. -1.0; 2.0; -"#; - let sysfile = sack(input, None, endian).unwrap(); +fn test_sysfile(name: &str) { + let input_filename = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("src/sys/testdata") + .join(name) + .with_extension("sack"); + let input = String::from_utf8(std::fs::read(&input_filename).unwrap()).unwrap(); + let expected_filename = input_filename.with_extension("expected"); + let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap(); + for endian in all::() { + let sysfile = sack(&input, Some(&input_filename), endian).unwrap(); let cursor = Cursor::new(sysfile); let reader = Reader::new(cursor, |warning| println!("{warning}")).unwrap(); let headers: Vec = reader.collect::, _>>().unwrap(); @@ -328,38 +67,6 @@ i8 0 *3; let mut errors = Vec::new(); let headers = Headers::new(decoded_records, &mut |e| errors.push(e)).unwrap(); let (dictionary, metadata) = decode(headers, encoding, |e| errors.push(e)).unwrap(); - assert_eq!(errors, vec![]); - /* - assert_sysfile( - &dictionary, - &metadata, - "\ - ╭──────────────────────┬────────────────────────╮ - │ Created │ 05-JAN-2011 20:53:52│ - ├──────────────────────┼────────────────────────┤ - │Writer Product │PSPP synthetic test file│ - │ Version │13.2.3 │ - ├──────────────────────┼────────────────────────┤ - │ Compression │None │ - │ Number of Cases│ 1│ - ╰──────────────────────┴────────────────────────╯ - - ╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮ - │ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│ - ├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤ - │num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ - │Numeric variable 2's label│ 2│Numeric variable 2's label│ │Input│ 8│Right │F8.0 │F8.0 │ │ - ╰──────────────────────────┴────────┴──────────────────────────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯ - ", - );*/ - - assert_eq!( - metadata, - Metadata { - version: Some((13, 2, 3)), - ..test_metadata(endian) - } - ); let (group, data) = metadata.to_pivot_rows(); let metadata_table = PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data( data.into_iter() @@ -367,261 +74,34 @@ i8 0 *3; .filter(|(_row, value)| !value.is_empty()) .map(|(row, value)| ([row], value)), ); - assert_rendering( - "wrong_variable_positions_but_v13_metadata", - &metadata_table, - "\ -╭──────────────────────┬────────────────────────╮ -│ Created │ 05-JAN-2011 20:53:52│ -├──────────────────────┼────────────────────────┤ -│Writer Product │PSPP synthetic test file│ -│ Version │13.2.3 │ -├──────────────────────┼────────────────────────┤ -│ Compression │None │ -│ Number of Cases│ 1│ -╰──────────────────────┴────────────────────────╯ -", - ); - assert_eq!( - dictionary.file_label.as_ref().map(|s| s.as_str()), - Some("PSPP synthetic test file") + let (group, data) = dictionary.to_pivot_rows(); + let dictionary_table = PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data( + data.into_iter() + .enumerate() + .filter(|(_row, value)| !value.is_empty()) + .map(|(row, value)| ([row], value)), ); - assert!(dictionary.output_value_labels().to_pivot_table().is_none()); - let pt = dictionary.output_variables().to_pivot_table(); - assert_rendering( - "value_labels_dictionary", - &pt, - "\ -╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮ -│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│ -├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤ -│num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ -│Numeric variable 2's label│ 2│Numeric variable 2's label│ │Input│ 8│Right │F8.0 │F8.0 │ │ -╰──────────────────────────┴────────┴──────────────────────────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯ -", + let mut output = Vec::new(); + output.extend( + errors + .into_iter() + .map(|error| Arc::new(Item::from(Text::new_log(error.to_string())))), ); - } -} - -#[test] -fn value_labels() { - for endian in all::() { - let input = r#" -# File header. -"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; -2; # Layout code -22; # Nominal case size -0; # Not compressed -0; # Not weighted -1; # 1 case. -100.0; # Bias. -"05 Jan 11"; "20:53:52"; s64 "PSPP synthetic test file"; -i8 0 *3; - -# Numeric variables. -2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; -2; 0; 0; 0; 0x050800 *2; s8 "NUM2"; -2; 0; 0; 0; 0x050800 *2; s8 "NUM3"; -2; 0; 0; 0; 0x050800 *2; s8 "NUM4"; -2; 0; 0; 0; 0x050800 *2; s8 "NUM5"; - -# String variables. -2; 1; 0; 0; 0x010100 *2; s8 "STR1"; # index 6 -2; 2; 0; 0; 0x010200 *2; s8 "STR2"; # index 7 -2; 3; 0; 0; 0x010300 *2; s8 "STR3"; # index 8 -2; 4; 0; 0; 0x010400 *2; s8 "STR4"; # index 9 -2; 4; 0; 0; 0x010400 *2; s8 "STR5"; # index 10 -2; 6; 0; 0; 0x010600 *2; s8 "STR6"; # index 11 -2; 7; 0; 0; 0x010700 *2; s8 "STR7"; # index 12 -2; 8; 0; 0; 0x010800 *2; s8 "STR8"; # index 13 -2; 9; 0; 0; 0x010900 *2; "STR9"; i8 230; s3 ""; # index 14 -2; -1; 0; 0; 0; 0; s8 ""; -2; 12; 0; 0; 0x010c00 *2; s8 "STR12"; # index 16 -2; -1; 0; 0; 0; 0; s8 ""; -2; 16; 0; 0; 0x011000 *2; s8 "STR16"; # index 18 -2; -1; 0; 0; 0; 0; s8 ""; -2; 17; 0; 0; 0x011100 *2; s8 "STR17"; # index 20 -( 2; -1; 0; 0; 0; 0; s8 ""; ) * 2; - -# One value label for NUM1. -3; 1; 1.0; i8 17; i8 238; i8 228; i8 232; i8 237; s19 " (in Russian)"; 4; 1; 1; - -# Two value labels for NUM2, as a single pair of type 3 and type 4 records. -3; 2; 1.0; i8 3; s7 "one"; 2.0; i8 3; s7 "two"; 4; 1; 2; - -# Two value labels for NUM3, as two pairs of type 3 and type 4 records. -3; 1; 3.0; i8 5; s7 "three"; 4; 1; 3; -3; 1; 4.0; i8 4; s7 "four"; 4; 1; 3; - -# Two common value labels for NUM4 and NUM5, plus two different ones for each. -3; 1; 5.0; i8 4; s7 "five"; 4; 1; 4; -3; 1; 6.0; i8 3; s7 "six"; 4; 1; 5; -3; 2; 7.0; i8 5; s7 "seven"; 8.0; i8 5; s7 "eight"; 4; 2; 4; 5; -3; 1; 9.0; i8 4; s7 "nine"; 4; 1; 4; -3; 1; 10.0; i8 3; s7 "ten"; 4; 1; 5; - -# One value label for STR1. -3; 1; s8 "a"; i8 19; s23 "value label for `a'"; 4; 1; 6; - -# Two value labels for STR2, as a single pair of type 3 and type 4 records. -3; 2; -s8 "bc"; i8 20; s23 "value label for `bc'"; -s8 "de"; i8 20; s23 "value label for `de'"; -4; 1; 7; - -# Two value labels for STR3, as two pairs of type 3 and type 4 records. -3; 1; s8 "fgh"; i8 21; s23 "value label for `fgh'"; 4; 1; 8; -3; 1; s8 "ijk"; i8 21; s23 "value label for `ijk'"; 4; 1; 8; - -# Two common value labels for STR4 and STR5, plus two different ones for each. -3; 1; s8 "lmno"; i8 22; s23 "value label for `lmno'"; 4; 1; 9; -3; 1; s8 "pqrs"; i8 22; s23 "value label for `pqrs'"; 4; 1; 10; -3; 2; -s8 "tuvw"; i8 22; s23 "value label for `tuvw'"; -s8 "xyzA"; i8 22; s23 "value label for `xyzA'"; -4; 2; 9; 10; -3; 1; s8 "BCDE"; i8 22; s23 "value label for `BCDE'"; 4; 1; 9; -3; 1; s8 "FGHI"; i8 22; s23 "value label for `FGHI'"; 4; 1; 10; - -# One value label for STR6, STR7, STR8. -3; 1; s8 "JKLMNO"; i8 24; s31 "value label for `JKLMNO'"; 4; 1; 11; -3; 1; s8 "JKLMNOP"; i8 25; s31 "value label for `JKLMNOP'"; 4; 1; 12; -3; 1; s8 "JKLMNOPQ"; i8 26; s31 "value label for `JKLMNOPQ'"; 4; 1; 13; - -# Machine integer info record. -7; 3; 4; 8; 1; 2; 3; -1; 1; 1; ENDIAN; 1251; - -# Character encoding record. -7; 20; 1; 12; "windows-1251"; - -7; 21; 1; COUNT ( -# One value label for STR9ж, -COUNT("STR9"; i8 230); 9; 1; COUNT("RSTUVWXYZ"); COUNT("value label for `RSTUVWXYZ'"); - -# Two value labels for STR12. -COUNT("STR12"); 12; 2; -COUNT("0123456789ab"); COUNT("value label for `0123456789ab'"); -COUNT("cdefghijklmn"); COUNT("value label for `cdefghijklmn'"); - -# Three value labels for STR16. -COUNT("STR16"); 16; 3; -COUNT("opqrstuvwxyzABCD"); COUNT("value label for `opqrstuvwxyzABCD'"); -COUNT("EFGHIJKLMNOPQRST"); COUNT("value label for `EFGHIJKLMNOPQRST'"); -COUNT("UVWXYZ0123456789"); COUNT("value label for `UVWXYZ0123456789' with Cyrillic letters: `"; i8 244; i8 245; i8 246; "'"); - -# One value label for STR17. -COUNT("STR17"); 17; 1; -COUNT("abcdefghijklmnopq"); COUNT("value label for `abcdefghijklmnopq'"); -); - -# Dictionary termination record. -999; 0; -"#; - let sysfile = sack(input, None, endian).unwrap(); - let cursor = Cursor::new(sysfile); - let reader = Reader::new(cursor, |warning| println!("{warning}")).unwrap(); - let headers: Vec = reader.collect::, _>>().unwrap(); - let encoding = encoding_from_headers(&headers, &|e| eprintln!("{e}")).unwrap(); - let decoder = Decoder::new(encoding, |e| eprintln!("{e}")); - let mut decoded_records = Vec::new(); - for header in headers { - decoded_records.push(header.decode(&decoder).unwrap()); + output.push(Arc::new(metadata_table.into())); + output.push(Arc::new(dictionary_table.into())); + output.push(Arc::new( + dictionary.output_variables().to_pivot_table().into(), + )); + if let Some(pt) = dictionary.output_value_labels().to_pivot_table() { + output.push(Arc::new(pt.into())); } + let output = Item::new(Details::Group(output)); - let mut errors = Vec::new(); - let headers = Headers::new(decoded_records, &mut |e| errors.push(e)).unwrap(); - let (dictionary, metadata) = decode(headers, encoding, |e| errors.push(e)).unwrap(); - assert_eq!(errors, vec![]); - assert_eq!(metadata, test_metadata(endian)); - assert_eq!( - dictionary.file_label.as_ref().map(|s| s.as_str()), - Some("PSPP synthetic test file") - ); - assert_rendering( - "value_labels_value_labels", - &dictionary.output_value_labels().to_pivot_table().unwrap(), - "\ -╭────────────────────────────────┬───────────────────────────────────────────────────────────────╮ -│Variable Value │ │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│num1 1 │один (in Russian) │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│num2 1 │one │ -│ 2 │two │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│num3 3 │three │ -│ 4 │four │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│num4 5 │five │ -│ 7 │seven │ -│ 8 │eight │ -│ 9 │nine │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│num5 6 │six │ -│ 7 │seven │ -│ 8 │eight │ -│ 10 │ten │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str1 a │value label for `a' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str2 bc │value label for `bc' │ -│ de │value label for `de' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str3 fgh │value label for `fgh' │ -│ ijk │value label for `ijk' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str4 BCDE │value label for `BCDE' │ -│ lmno │value label for `lmno' │ -│ tuvw │value label for `tuvw' │ -│ xyzA │value label for `xyzA' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str5 FGHI │value label for `FGHI' │ -│ pqrs │value label for `pqrs' │ -│ tuvw │value label for `tuvw' │ -│ xyzA │value label for `xyzA' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str6 JKLMNO │value label for `JKLMNO' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str7 JKLMNOP │value label for `JKLMNOP' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str8 JKLMNOPQ │value label for `JKLMNOPQ' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str9ж RSTUVWXYZ │value label for `RSTUVWXYZ' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str12 0123456789ab │value label for `0123456789ab' │ -│ cdefghijklmn │value label for `cdefghijklmn' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str16 EFGHIJKLMNOPQRST │value label for `EFGHIJKLMNOPQRST' │ -│ UVWXYZ0123456789 │value label for `UVWXYZ0123456789' with Cyrillic letters: `фхц'│ -│ opqrstuvwxyzABCD │value label for `opqrstuvwxyzABCD' │ -├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ -│str17 abcdefghijklmnopq│value label for `abcdefghijklmnopq' │ -╰────────────────────────────────┴───────────────────────────────────────────────────────────────╯ -", + assert_lines_eq( + &expected, + expected_filename.display(), + &output.to_string(), + "actual", ); - let pt = dictionary.output_variables().to_pivot_table(); - assert_rendering("value_labels_dictionary", &pt, "\ -╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮ -│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│ -├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤ -│num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ -│num2 │ 2│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ -│num3 │ 3│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ -│num4 │ 4│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ -│num5 │ 5│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ -│str1 │ 6│ │Nominal │Input│ 1│Left │A1 │A1 │ │ -│str2 │ 7│ │Nominal │Input│ 2│Left │A2 │A2 │ │ -│str3 │ 8│ │Nominal │Input│ 3│Left │A3 │A3 │ │ -│str4 │ 9│ │Nominal │Input│ 4│Left │A4 │A4 │ │ -│str5 │ 10│ │Nominal │Input│ 4│Left │A4 │A4 │ │ -│str6 │ 11│ │Nominal │Input│ 6│Left │A6 │A6 │ │ -│str7 │ 12│ │Nominal │Input│ 7│Left │A7 │A7 │ │ -│str8 │ 13│ │Nominal │Input│ 8│Left │A8 │A8 │ │ -│str9ж│ 14│ │Nominal │Input│ 9│Left │A9 │A9 │ │ -│str12│ 15│ │Nominal │Input│ 12│Left │A12 │A12 │ │ -│str16│ 16│ │Nominal │Input│ 16│Left │A16 │A16 │ │ -│str17│ 17│ │Nominal │Input│ 17│Left │A17 │A17 │ │ -╰─────┴────────┴─────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯ -"); } } diff --git a/rust/pspp/src/sys/testdata/documents.expected b/rust/pspp/src/sys/testdata/documents.expected new file mode 100644 index 0000000000..65439d5039 --- /dev/null +++ b/rust/pspp/src/sys/testdata/documents.expected @@ -0,0 +1,24 @@ +╭──────────────────────┬────────────────────────╮ +│ Created │ 01-JAN-2011 20:53:52│ +├──────────────────────┼────────────────────────┤ +│Writer Product │PSPP synthetic test file│ +│ Version │1.2.3 │ +├──────────────────────┼────────────────────────┤ +│ Compression │None │ +│ Number of Cases│ 1│ +╰──────────────────────┴────────────────────────╯ + +╭─────────┬───────────────────────────────────────────────────────╮ +│Label │PSPP synthetic test file │ +│Variables│ 1│ +│Documents│First line of documents │ +│ │Second line of documents │ +│ │abbé appliqué attaché blasé café canapé cliché consommé│ +│ │Last line of documents │ +╰─────────┴───────────────────────────────────────────────────────╯ + +╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮ +│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│ +├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤ +│num1│ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +╰────┴────────┴─────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯ diff --git a/rust/pspp/src/sys/testdata/documents.sack b/rust/pspp/src/sys/testdata/documents.sack new file mode 100644 index 0000000000..3ecaaf0fcf --- /dev/null +++ b/rust/pspp/src/sys/testdata/documents.sack @@ -0,0 +1,34 @@ +# File header. +"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; +2; # Layout code +1; # Nominal case size +0; # Not compressed +0; # Not weighted +1; # 1 case. +100.0; # Bias. +"01 Jan 11"; "20:53:52"; s64 "PSPP synthetic test file"; +i8 0 *3; + +# Numeric variable, no label or missing values. +2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; + +# Machine integer info record. +7; 3; 4; 8; 1; 2; 3; -1; 1; 1; ENDIAN; 1252; + +# Document record. +6; 5; +s80 "First line of documents"; +s80 "Second line of documents"; +"abb"; i8 233; " appliqu"; i8 233; " attach"; i8 233; " blas"; i8 233; " caf"; i8 233; " canap"; i8 233; " clich"; i8 233; " consomm"; i8 233; +s25 ""; +s80 ""; +s80 "Last line of documents"; + +# Character encoding record. +7; 20; 1; 12; "windows-1252"; + +# Dictionary termination record. +999; 0; + +# Data. +1.0; diff --git a/rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.expected b/rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.expected new file mode 100644 index 0000000000..bdb0b0da07 --- /dev/null +++ b/rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.expected @@ -0,0 +1,20 @@ +╭──────────────────────┬────────────────────────╮ +│ Created │ 05-JAN-2011 20:53:52│ +├──────────────────────┼────────────────────────┤ +│Writer Product │PSPP synthetic test file│ +├──────────────────────┼────────────────────────┤ +│ Compression │None │ +│ Number of Cases│ 1│ +╰──────────────────────┴────────────────────────╯ + +╭─────────┬────────────────────────╮ +│Label │PSPP synthetic test file│ +│Variables│ 2│ +╰─────────┴────────────────────────╯ + +╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮ +│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│ +├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤ +│num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +│Numeric variable 2's label│ 2│Numeric variable 2's label│ │Input│ 8│Right │F8.0 │F8.0 │ │ +╰──────────────────────────┴────────┴──────────────────────────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯ diff --git a/rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.sack b/rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.sack new file mode 100644 index 0000000000..53b12bce74 --- /dev/null +++ b/rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.sack @@ -0,0 +1,26 @@ +# File header. +"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; +2; # Layout code +-1; # Nominal case size (unspecified) +0; # Not compressed +0; # Not weighted +1; # 1 case. +100.0; # Bias. +"05 Jan 11"; "20:53:52"; s64 "PSPP synthetic test file"; +i8 0 *3; + +# Numeric variable, no label or missing values. +2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; + +# Numeric variable, variable label. +2; 0; 1; 0; 0x050800 *2; s8 "NUM2"; +26; "Numeric variable 2's label"; i8 0 *2; + +# Character encoding record. +7; 20; 1; 12; "windows-1252"; + +# Dictionary termination record. +999; 0; + +# Data. +1.0; 2.0; diff --git a/rust/pspp/src/sys/testdata/value_labels.expected b/rust/pspp/src/sys/testdata/value_labels.expected new file mode 100644 index 0000000000..c62f7006a2 --- /dev/null +++ b/rust/pspp/src/sys/testdata/value_labels.expected @@ -0,0 +1,93 @@ +╭──────────────────────┬────────────────────────╮ +│ Created │ 05-JAN-2011 20:53:52│ +├──────────────────────┼────────────────────────┤ +│Writer Product │PSPP synthetic test file│ +│ Version │1.2.3 │ +├──────────────────────┼────────────────────────┤ +│ Compression │None │ +│ Number of Cases│ 1│ +╰──────────────────────┴────────────────────────╯ + +╭─────────┬────────────────────────╮ +│Label │PSPP synthetic test file│ +│Variables│ 17│ +╰─────────┴────────────────────────╯ + +╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮ +│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│ +├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤ +│num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +│num2 │ 2│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +│num3 │ 3│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +│num4 │ 4│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +│num5 │ 5│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +│str1 │ 6│ │Nominal │Input│ 1│Left │A1 │A1 │ │ +│str2 │ 7│ │Nominal │Input│ 2│Left │A2 │A2 │ │ +│str3 │ 8│ │Nominal │Input│ 3│Left │A3 │A3 │ │ +│str4 │ 9│ │Nominal │Input│ 4│Left │A4 │A4 │ │ +│str5 │ 10│ │Nominal │Input│ 4│Left │A4 │A4 │ │ +│str6 │ 11│ │Nominal │Input│ 6│Left │A6 │A6 │ │ +│str7 │ 12│ │Nominal │Input│ 7│Left │A7 │A7 │ │ +│str8 │ 13│ │Nominal │Input│ 8│Left │A8 │A8 │ │ +│str9ж│ 14│ │Nominal │Input│ 9│Left │A9 │A9 │ │ +│str12│ 15│ │Nominal │Input│ 12│Left │A12 │A12 │ │ +│str16│ 16│ │Nominal │Input│ 16│Left │A16 │A16 │ │ +│str17│ 17│ │Nominal │Input│ 17│Left │A17 │A17 │ │ +╰─────┴────────┴─────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯ + +╭────────────────────────────────┬───────────────────────────────────────────────────────────────╮ +│Variable Value │ │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│num1 1 │один (in Russian) │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│num2 1 │one │ +│ 2 │two │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│num3 3 │three │ +│ 4 │four │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│num4 5 │five │ +│ 7 │seven │ +│ 8 │eight │ +│ 9 │nine │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│num5 6 │six │ +│ 7 │seven │ +│ 8 │eight │ +│ 10 │ten │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str1 a │value label for `a' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str2 bc │value label for `bc' │ +│ de │value label for `de' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str3 fgh │value label for `fgh' │ +│ ijk │value label for `ijk' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str4 BCDE │value label for `BCDE' │ +│ lmno │value label for `lmno' │ +│ tuvw │value label for `tuvw' │ +│ xyzA │value label for `xyzA' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str5 FGHI │value label for `FGHI' │ +│ pqrs │value label for `pqrs' │ +│ tuvw │value label for `tuvw' │ +│ xyzA │value label for `xyzA' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str6 JKLMNO │value label for `JKLMNO' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str7 JKLMNOP │value label for `JKLMNOP' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str8 JKLMNOPQ │value label for `JKLMNOPQ' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str9ж RSTUVWXYZ │value label for `RSTUVWXYZ' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str12 0123456789ab │value label for `0123456789ab' │ +│ cdefghijklmn │value label for `cdefghijklmn' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str16 EFGHIJKLMNOPQRST │value label for `EFGHIJKLMNOPQRST' │ +│ UVWXYZ0123456789 │value label for `UVWXYZ0123456789' with Cyrillic letters: `фхц'│ +│ opqrstuvwxyzABCD │value label for `opqrstuvwxyzABCD' │ +├────────────────────────────────┼───────────────────────────────────────────────────────────────┤ +│str17 abcdefghijklmnopq│value label for `abcdefghijklmnopq' │ +╰────────────────────────────────┴───────────────────────────────────────────────────────────────╯ diff --git a/rust/pspp/src/sys/testdata/value_labels.sack b/rust/pspp/src/sys/testdata/value_labels.sack new file mode 100644 index 0000000000..8198711e1e --- /dev/null +++ b/rust/pspp/src/sys/testdata/value_labels.sack @@ -0,0 +1,109 @@ +# File header. +"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; +2; # Layout code +22; # Nominal case size +0; # Not compressed +0; # Not weighted +1; # 1 case. +100.0; # Bias. +"05 Jan 11"; "20:53:52"; s64 "PSPP synthetic test file"; +i8 0 *3; + +# Numeric variables. +2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; +2; 0; 0; 0; 0x050800 *2; s8 "NUM2"; +2; 0; 0; 0; 0x050800 *2; s8 "NUM3"; +2; 0; 0; 0; 0x050800 *2; s8 "NUM4"; +2; 0; 0; 0; 0x050800 *2; s8 "NUM5"; + +# String variables. +2; 1; 0; 0; 0x010100 *2; s8 "STR1"; # index 6 +2; 2; 0; 0; 0x010200 *2; s8 "STR2"; # index 7 +2; 3; 0; 0; 0x010300 *2; s8 "STR3"; # index 8 +2; 4; 0; 0; 0x010400 *2; s8 "STR4"; # index 9 +2; 4; 0; 0; 0x010400 *2; s8 "STR5"; # index 10 +2; 6; 0; 0; 0x010600 *2; s8 "STR6"; # index 11 +2; 7; 0; 0; 0x010700 *2; s8 "STR7"; # index 12 +2; 8; 0; 0; 0x010800 *2; s8 "STR8"; # index 13 +2; 9; 0; 0; 0x010900 *2; "STR9"; i8 230; s3 ""; # index 14 +2; -1; 0; 0; 0; 0; s8 ""; +2; 12; 0; 0; 0x010c00 *2; s8 "STR12"; # index 16 +2; -1; 0; 0; 0; 0; s8 ""; +2; 16; 0; 0; 0x011000 *2; s8 "STR16"; # index 18 +2; -1; 0; 0; 0; 0; s8 ""; +2; 17; 0; 0; 0x011100 *2; s8 "STR17"; # index 20 +( 2; -1; 0; 0; 0; 0; s8 ""; ) * 2; + +# One value label for NUM1. +3; 1; 1.0; i8 17; i8 238; i8 228; i8 232; i8 237; s19 " (in Russian)"; 4; 1; 1; + +# Two value labels for NUM2, as a single pair of type 3 and type 4 records. +3; 2; 1.0; i8 3; s7 "one"; 2.0; i8 3; s7 "two"; 4; 1; 2; + +# Two value labels for NUM3, as two pairs of type 3 and type 4 records. +3; 1; 3.0; i8 5; s7 "three"; 4; 1; 3; +3; 1; 4.0; i8 4; s7 "four"; 4; 1; 3; + +# Two common value labels for NUM4 and NUM5, plus two different ones for each. +3; 1; 5.0; i8 4; s7 "five"; 4; 1; 4; +3; 1; 6.0; i8 3; s7 "six"; 4; 1; 5; +3; 2; 7.0; i8 5; s7 "seven"; 8.0; i8 5; s7 "eight"; 4; 2; 4; 5; +3; 1; 9.0; i8 4; s7 "nine"; 4; 1; 4; +3; 1; 10.0; i8 3; s7 "ten"; 4; 1; 5; + +# One value label for STR1. +3; 1; s8 "a"; i8 19; s23 "value label for `a'"; 4; 1; 6; + +# Two value labels for STR2, as a single pair of type 3 and type 4 records. +3; 2; +s8 "bc"; i8 20; s23 "value label for `bc'"; +s8 "de"; i8 20; s23 "value label for `de'"; +4; 1; 7; + +# Two value labels for STR3, as two pairs of type 3 and type 4 records. +3; 1; s8 "fgh"; i8 21; s23 "value label for `fgh'"; 4; 1; 8; +3; 1; s8 "ijk"; i8 21; s23 "value label for `ijk'"; 4; 1; 8; + +# Two common value labels for STR4 and STR5, plus two different ones for each. +3; 1; s8 "lmno"; i8 22; s23 "value label for `lmno'"; 4; 1; 9; +3; 1; s8 "pqrs"; i8 22; s23 "value label for `pqrs'"; 4; 1; 10; +3; 2; +s8 "tuvw"; i8 22; s23 "value label for `tuvw'"; +s8 "xyzA"; i8 22; s23 "value label for `xyzA'"; +4; 2; 9; 10; +3; 1; s8 "BCDE"; i8 22; s23 "value label for `BCDE'"; 4; 1; 9; +3; 1; s8 "FGHI"; i8 22; s23 "value label for `FGHI'"; 4; 1; 10; + +# One value label for STR6, STR7, STR8. +3; 1; s8 "JKLMNO"; i8 24; s31 "value label for `JKLMNO'"; 4; 1; 11; +3; 1; s8 "JKLMNOP"; i8 25; s31 "value label for `JKLMNOP'"; 4; 1; 12; +3; 1; s8 "JKLMNOPQ"; i8 26; s31 "value label for `JKLMNOPQ'"; 4; 1; 13; + +# Machine integer info record. +7; 3; 4; 8; 1; 2; 3; -1; 1; 1; ENDIAN; 1251; + +# Character encoding record. +7; 20; 1; 12; "windows-1251"; + +7; 21; 1; COUNT ( +# One value label for STR9ж, +COUNT("STR9"; i8 230); 9; 1; COUNT("RSTUVWXYZ"); COUNT("value label for `RSTUVWXYZ'"); + +# Two value labels for STR12. +COUNT("STR12"); 12; 2; +COUNT("0123456789ab"); COUNT("value label for `0123456789ab'"); +COUNT("cdefghijklmn"); COUNT("value label for `cdefghijklmn'"); + +# Three value labels for STR16. +COUNT("STR16"); 16; 3; +COUNT("opqrstuvwxyzABCD"); COUNT("value label for `opqrstuvwxyzABCD'"); +COUNT("EFGHIJKLMNOPQRST"); COUNT("value label for `EFGHIJKLMNOPQRST'"); +COUNT("UVWXYZ0123456789"); COUNT("value label for `UVWXYZ0123456789' with Cyrillic letters: `"; i8 244; i8 245; i8 246; "'"); + +# One value label for STR17. +COUNT("STR17"); 17; 1; +COUNT("abcdefghijklmnopq"); COUNT("value label for `abcdefghijklmnopq'"); +); + +# Dictionary termination record. +999; 0; diff --git a/rust/pspp/src/sys/testdata/variable_labels_and_missing_values.expected b/rust/pspp/src/sys/testdata/variable_labels_and_missing_values.expected new file mode 100644 index 0000000000..f3c6549215 --- /dev/null +++ b/rust/pspp/src/sys/testdata/variable_labels_and_missing_values.expected @@ -0,0 +1,40 @@ +╭──────────────────────┬────────────────────────╮ +│ Created │ 05-JAN-2011 20:53:52│ +├──────────────────────┼────────────────────────┤ +│Writer Product │PSPP synthetic test file│ +│ Version │1.2.3 │ +├──────────────────────┼────────────────────────┤ +│ Compression │None │ +│ Number of Cases│ 1│ +╰──────────────────────┴────────────────────────╯ + +╭─────────┬──────────────────────────────╮ +│Label │PSPP synthetic test file: ôõöø│ +│Variables│ 21│ +╰─────────┴──────────────────────────────╯ + +╭────────────────────────────────┬────────┬────────────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────────────╮ +│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│ Missing Values │ +├────────────────────────────────┼────────┼────────────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────────────┤ +│num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +│Numeric variable 2's label (ùúû)│ 2│Numeric variable 2's label (ùúû)│ │Input│ 8│Right │F8.0 │F8.0 │ │ +│num3 │ 3│ │ │Input│ 8│Right │F8.0 │F8.0 │1 │ +│Another numeric variable label │ 4│Another numeric variable label │ │Input│ 8│Right │F8.0 │F8.0 │1 │ +│num5 │ 5│ │ │Input│ 8│Right │F8.0 │F8.0 │1; 2 │ +│num6 │ 6│ │ │Input│ 8│Right │F8.0 │F8.0 │1; 2; 3 │ +│num7 │ 7│ │ │Input│ 8│Right │F8.0 │F8.0 │1 THRU 3 │ +│num8 │ 8│ │ │Input│ 8│Right │F8.0 │F8.0 │1 THRU 3; 5 │ +│num9 │ 9│ │ │Input│ 8│Right │F8.0 │F8.0 │1 THRU HIGH; -5 │ +│numàèìñò │ 10│ │ │Input│ 8│Right │F8.0 │F8.0 │LOW THRU 1; 5 │ +│str1 │ 11│ │Nominal │Input│ 4│Left │A4 │A4 │ │ +│String variable 2's label │ 12│String variable 2's label │Nominal │Input│ 4│Left │A4 │A4 │ │ +│str3 │ 13│ │Nominal │Input│ 4│Left │A4 │A4 │"MISS" │ +│Another string variable label │ 14│Another string variable label │Nominal │Input│ 4│Left │A4 │A4 │"OTHR" │ +│str5 │ 15│ │Nominal │Input│ 4│Left │A4 │A4 │"MISS"; "OTHR" │ +│str6 │ 16│ │Nominal │Input│ 4│Left │A4 │A4 │"MISS"; "OTHR"; "MORE"│ +│str7 │ 17│ │Nominal │Input│ 11│Left │A11 │A11 │"first8by" │ +│str8 │ 18│ │Nominal │Input│ 9│Left │A9 │A9 │ │ +│str9 │ 19│ │Nominal │Input│ 10│Left │A10 │A10 │ │ +│str10 │ 20│ │Nominal │Input│ 11│Left │A11 │A11 │ │ +│25-byte string │ 21│25-byte string │Nominal │Input│ 25│Left │A25 │A25 │ │ +╰────────────────────────────────┴────────┴────────────────────────────────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────────────╯ diff --git a/rust/pspp/src/sys/testdata/variable_labels_and_missing_values.sack b/rust/pspp/src/sys/testdata/variable_labels_and_missing_values.sack new file mode 100644 index 0000000000..3f8f22fd9b --- /dev/null +++ b/rust/pspp/src/sys/testdata/variable_labels_and_missing_values.sack @@ -0,0 +1,117 @@ +# File header. +"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; +2; # Layout code +28; # Nominal case size +0; # Not compressed +0; # Not weighted +1; # 1 case. +100.0; # Bias. +"05 Jan 11"; "20:53:52"; +"PSPP synthetic test file: "; i8 244; i8 245; i8 246; i8 248; s34 ""; +i8 0 *3; + +# Numeric variable, no label or missing values. +2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; + +# Numeric variable, variable label. +2; 0; 1; 0; 0x050800 *2; s8 "NUM2"; +32; "Numeric variable 2's label ("; i8 249; i8 250; i8 251; ")"; + +# Numeric variable, one missing value. +2; 0; 0; 1; 0x050800 *2; s8 "NUM3"; +1.0; + +# Numeric variable, variable label and missing value. +2; 0; 1; 1; 0x050800 *2; s8 "NUM4"; +30; "Another numeric variable label"; i8 0 * 2; +1.0; + +# Numeric variable, two missing values. +2; 0; 0; 2; 0x050800 *2; s8 "NUM5"; 1.0; 2.0; + +# Numeric variable, three missing values. +2; 0; 0; 3; 0x050800 *2; s8 "NUM6"; 1.0; 2.0; 3.0; + +# Numeric variable, range of missing values. +2; 0; 0; -2; 0x050800 *2; s8 "NUM7"; 1.0; 3.0; + +# Numeric variables, range of missing values plus discrete value. +2; 0; 0; -3; 0x050800 *2; s8 "NUM8"; 1.0; 3.0; 5.0; +2; 0; 0; -3; 0x050800 *2; s8 "NUM9"; 1.0; HIGHEST; -5.0; +2; 0; 0; -3; 0x050800 *2; "NUM"; i8 192; i8 200; i8 204; i8 209; i8 210; +LOWEST; 1.0; 5.0; + +# String variable, no label or missing values. +2; 4; 0; 0; 0x010400 *2; s8 "STR1"; + +# String variable, variable label. +2; 4; 1; 0; 0x010400 *2; s8 "STR2"; +25; "String variable 2's label"; i8 0 * 3; + +# String variable, one missing value. +2; 4; 0; 1; 0x010400 *2; s8 "STR3"; s8 "MISS"; + +# String variable, variable label and missing value. +2; 4; 1; 1; 0x010400 *2; s8 "STR4"; +29; "Another string variable label"; i8 0 * 3; +s8 "OTHR"; + +# String variable, two missing values. +2; 4; 0; 2; 0x010400 *2; s8 "STR5"; s8 "MISS"; s8 "OTHR"; + +# String variable, three missing values. +2; 4; 0; 3; 0x010400 *2; s8 "STR6"; s8 "MISS"; s8 "OTHR"; s8 "MORE"; + +# Long string variable, one missing value. +# (This is not how SPSS represents missing values for long strings--it +# uses a separate record as shown later below--but old versions of PSPP +# did use this representation so we continue supporting it for backward +# compatibility. +2; 11; 0; 1; 0x010b00 *2; s8 "STR7"; "first8by"; +2; -1; 0; 0; 0; 0; s8 ""; + +# Long string variables that will have missing values added with a +# later record. +2; 9; 0; 0; 0x010900 *2; s8 "STR8"; +2; -1; 0; 0; 0; 0; s8 ""; +2; 10; 0; 0; 0x010a00 *2; s8 "STR9"; +2; -1; 0; 0; 0; 0; s8 ""; +2; 11; 0; 0; 0x010b00 *2; s8 "STR10"; +2; -1; 0; 0; 0; 0; s8 ""; + +# Long string variable, value label. +2; 25; 1; 0; 0x011900 *2; s8 "STR11"; 14; "25-byte string"; i8 0 * 2; +( 2; -1; 0; 0; 0; 0; s8 ""; ) * 2; +# Variable label fields on continuation records have been spotted in system +# files created by "SPSS Power Macintosh Release 6.1". +2; -1; 1; 0; 0; 0; s8 ""; 20; "dummy variable label"; + +# Machine integer info record. +7; 3; 4; 8; 1; 2; 3; -1; 1; 1; ENDIAN; 1252; + +# Machine floating-point info record. +7; 4; 8; 3; SYSMIS; HIGHEST; LOWEST; + +# Long string variable missing values record. +7; 22; 1; COUNT ( +# One missing value for STR8. +COUNT("STR8"); i8 1; 8; "abcdefgh"; + +# Two missing values for STR9. +COUNT("STR9"); i8 2; 8; "abcdefgh"; "01234567"; + +# Three missing values for STR9. +COUNT("STR10"); i8 3; 8; "abcdefgh"; "01234567"; "0 "; +); + +# Character encoding record. +7; 20; 1; 12; "windows-1252"; + +# Dictionary termination record. +999; 0; + +# Data. +1.0; 2.0; 3.0; 4.0; 5.0; 6.0; 7.0; 8.0; 9.0; 10.0; +s8 "abcd"; s8 "efgh"; s8 "ijkl"; s8 "mnop"; s8 "qrst"; s8 "uvwx"; +s16 "yzABCDEFGHI"; s16 "JKLMNOPQR"; s16 "STUVWXYZ01"; +s16 "23456789abc"; s32 "defghijklmnopqstuvwxyzABC"; diff --git a/rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.expected b/rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.expected new file mode 100644 index 0000000000..119a3389d8 --- /dev/null +++ b/rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.expected @@ -0,0 +1,21 @@ +╭──────────────────────┬────────────────────────╮ +│ Created │ 05-JAN-2011 20:53:52│ +├──────────────────────┼────────────────────────┤ +│Writer Product │PSPP synthetic test file│ +│ Version │13.2.3 │ +├──────────────────────┼────────────────────────┤ +│ Compression │None │ +│ Number of Cases│ 1│ +╰──────────────────────┴────────────────────────╯ + +╭─────────┬────────────────────────╮ +│Label │PSPP synthetic test file│ +│Variables│ 2│ +╰─────────┴────────────────────────╯ + +╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮ +│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│ +├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤ +│num1 │ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │ +│Numeric variable 2's label│ 2│Numeric variable 2's label│ │Input│ 8│Right │F8.0 │F8.0 │ │ +╰──────────────────────────┴────────┴──────────────────────────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯ diff --git a/rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.sack b/rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.sack new file mode 100644 index 0000000000..a975825b2e --- /dev/null +++ b/rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.sack @@ -0,0 +1,29 @@ +# File header. +"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file"; +2; # Layout code +-1; # Nominal case size (unspecified) +0; # Not compressed +0; # Not weighted +1; # 1 case. +100.0; # Bias. +"05 Jan 11"; "20:53:52"; s64 "PSPP synthetic test file"; +i8 0 *3; + +# Numeric variable, no label or missing values. +2; 0; 0; 0; 0x050800 *2; s8 "NUM1"; + +# Numeric variable, variable label. +2; 0; 1; 0; 0x050800 *2; s8 "NUM2"; +26; "Numeric variable 2's label"; i8 0 *2; + +# Machine integer info record (SPSS 13). +7; 3; 4; 8; 13; 2; 3; -1; 1; 1; ENDIAN; 1252; + +# Character encoding record. +7; 20; 1; 12; "windows-1252"; + +# Dictionary termination record. +999; 0; + +# Data. +1.0; 2.0; diff --git a/rust/pspp/tests/sack.rs b/rust/pspp/tests/sack.rs index 5be80ea45f..d94016b264 100644 --- a/rust/pspp/tests/sack.rs +++ b/rust/pspp/tests/sack.rs @@ -83,7 +83,7 @@ fn main() -> Result<()> { let input = read_to_string(&input_file_name) .map_err(|err| anyhow!("{input_file_str}: read failed ({err})"))?; - let output = sack(&input, Some(&input_file_str), endian)?; + let output = sack(&input, Some(&input_file_name), endian)?; let output_file_str = output_file_name.to_string_lossy(); std::fs::write(&output_file_name, output) -- 2.30.2