more tests
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 9 Jun 2025 21:47:30 +0000 (14:47 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 9 Jun 2025 21:47:30 +0000 (14:47 -0700)
rust/pspp/src/output/mod.rs
rust/pspp/src/output/pivot/mod.rs
rust/pspp/src/output/text.rs
rust/pspp/src/sys/test.rs
rust/pspp/src/sys/testdata/compressed_data.expected [new file with mode: 0644]
rust/pspp/src/sys/testdata/compressed_data.sack [new file with mode: 0644]
rust/pspp/src/sys/testdata/variable_roles.expected [new file with mode: 0644]
rust/pspp/src/sys/testdata/variable_roles.sack [new file with mode: 0644]

index ff054b9e4619a7075cb5da259d49fc1d3c067928..a39cb9da197ed09a3b324fdc736848e0aa3ea005 100644 (file)
@@ -1,9 +1,16 @@
 #![allow(dead_code)]
-use std::{borrow::Cow, sync::Arc};
+use std::{
+    borrow::Cow,
+    sync::{Arc, OnceLock},
+};
 
+use enum_map::EnumMap;
 use pivot::PivotTable;
 
-use crate::message::Diagnostic;
+use crate::{
+    message::Diagnostic,
+    output::pivot::{Axis3, BorderStyle, Dimension, Group, Look},
+};
 
 use self::pivot::Value;
 
@@ -157,6 +164,7 @@ impl From<Box<Text>> for Details {
     }
 }
 
+#[derive(Clone, Debug)]
 pub struct Text {
     type_: TextType,
 
@@ -172,12 +180,39 @@ impl Text {
     }
 }
 
+fn text_item_table_look() -> Arc<Look> {
+    static LOOK: OnceLock<Arc<Look>> = OnceLock::new();
+    LOOK.get_or_init(|| {
+        Arc::new({
+            let mut look = Look::default().with_borders(EnumMap::from_fn(|_| BorderStyle::none()));
+            for style in look.areas.values_mut() {
+                style.cell_style.margins = EnumMap::from_fn(|_| [0, 0]);
+            }
+            look
+        })
+    })
+    .clone()
+}
+
+impl From<Text> for PivotTable {
+    fn from(value: Text) -> Self {
+        let dimension =
+            Dimension::new(Group::new(Value::new_text("Text")).with(Value::new_user_text("null")))
+                .with_all_labels_hidden();
+        PivotTable::new([(Axis3::Y, dimension)])
+            .with_look(text_item_table_look())
+            .with_data([(&[0], value.content)])
+            .with_subtype(Value::new_user_text("Text"))
+    }
+}
+
 impl From<&Diagnostic> for Text {
     fn from(value: &Diagnostic) -> Self {
         Self::new_log(value.to_string())
     }
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum TextType {
     /// `TITLE` and `SUBTITLE` commands.
     PageTitle,
index ce2db04c91c9799a011dd57428ef12f529813dfb..2fb4dfa773582df52e25ae23e17f1ae64577b967 100644 (file)
@@ -375,6 +375,13 @@ impl Dimension {
     pub fn leaf_path(&self, index: usize) -> Option<Path<'_>> {
         self.root.leaf_path(index, SmallVec::new())
     }
+
+    pub fn with_all_labels_hidden(self) -> Self {
+        Self {
+            hide_all_labels: true,
+            ..self
+        }
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -1334,6 +1341,13 @@ impl PivotTable {
         self
     }
 
+    pub fn with_subtype(self, subtype: Value) -> Self {
+        Self {
+            subtype: Some(Box::new(subtype)),
+            ..self
+        }
+    }
+
     pub fn with_show_title(mut self, show_title: bool) -> Self {
         self.show_title = show_title;
         self
@@ -1486,7 +1500,7 @@ impl PivotTable {
         self.cells.get(&self.cell_index(data_indexes))
     }
 
-    pub fn with_data<I>(mut self, iter: impl Iterator<Item = (I, Value)>) -> Self
+    pub fn with_data<I>(mut self, iter: impl IntoIterator<Item = (I, Value)>) -> Self
     where
         I: AsRef<[usize]>,
     {
index c29fad05451836f884186ab72ba4f0593b48cd7b..8f556454b3ed5acd78ba154a9d9d949fa8dc9060 100644 (file)
@@ -370,7 +370,7 @@ impl TextRenderer {
             Details::Message(_diagnostic) => todo!(),
             Details::PageBreak => Ok(()),
             Details::Table(pivot_table) => self.render_table(&*pivot_table, writer),
-            Details::Text(_text) => todo!(),
+            Details::Text(text) => self.render_table(&PivotTable::from((**text).clone()), writer),
         }
     }
 
index 69e1af3e1c103601ff09c9bc7f676ff1f9ff44e2..e5f5f914fe70c7dc3085250c357a2e4c93560db3 100644 (file)
@@ -87,6 +87,16 @@ fn attributes() {
     test_sysfile("attributes");
 }
 
+#[test]
+fn variable_roles() {
+    test_sysfile("variable_roles");
+}
+
+#[test]
+fn compressed_data() {
+    test_sysfile("compressed_data");
+}
+
 fn test_sysfile(name: &str) {
     let input_filename = Path::new(env!("CARGO_MANIFEST_DIR"))
         .join("src/sys/testdata")
diff --git a/rust/pspp/src/sys/testdata/compressed_data.expected b/rust/pspp/src/sys/testdata/compressed_data.expected
new file mode 100644 (file)
index 0000000..f36fb5e
--- /dev/null
@@ -0,0 +1,23 @@
+╭──────────────────────┬────────────────────────╮
+│       Created        │    01-JAN-2011 20:53:52│
+├──────────────────────┼────────────────────────┤
+│Writer Product        │PSPP synthetic test file│
+├──────────────────────┼────────────────────────┤
+│       Compression    │SAV                     │
+│       Number of Cases│Unknown                 │
+╰──────────────────────┴────────────────────────╯
+
+╭─────────┬────────────────────────╮
+│Label    │PSPP synthetic test file│
+│Variables│                       5│
+╰─────────┴────────────────────────╯
+
+╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
+│     │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        │              │
+│str4 │       3│     │Nominal          │Input│    4│Left     │A4          │A4          │              │
+│str8 │       4│     │Nominal          │Input│    8│Left     │A8          │A8          │              │
+│str15│       5│     │Nominal          │Input│   15│Left     │A15         │A15         │              │
+╰─────┴────────┴─────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯
diff --git a/rust/pspp/src/sys/testdata/compressed_data.sack b/rust/pspp/src/sys/testdata/compressed_data.sack
new file mode 100644 (file)
index 0000000..fec8358
--- /dev/null
@@ -0,0 +1,32 @@
+# File header.
+"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file";
+2; # Layout code
+6; # Nominal case size
+1; # Simple compression
+0; # Not weighted
+-1; # Unspecified number of cases.
+100.0; # Bias.
+"01 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";
+
+# String variable.
+2; 4; 0; 0; 0x010400 *2; s8 "STR4";
+2; 8; 0; 0; 0x010800 *2; s8 "STR8";
+2; 15; 0; 0; 0x010f00 *2; s8 "STR15";
+2; -1; 0; 0; 0; 0; s8 "";
+
+# Character encoding record.
+7; 20; 1; 12; "windows-1252";
+
+# Dictionary termination record.
+999; 0;
+
+# Compressed data.
+i8 1 100 254 253 254 253; i8 255 251; "abcdefgh"; s8 "0123";
+i8 253 253 253 254; i8 101 102 253 253; s8 "jklm"; s8 "nopqrstu";
+s8 "vwxyzABC"; s8 "DEFG"; s8 "HIJKLMNO";
+i8 254 253 252 0 0 0 0 0; s8 "PQRSTUVW";
diff --git a/rust/pspp/src/sys/testdata/variable_roles.expected b/rust/pspp/src/sys/testdata/variable_roles.expected
new file mode 100644 (file)
index 0000000..ce3c4cc
--- /dev/null
@@ -0,0 +1,46 @@
+Unknown role "6".
+
+╭──────────────────────┬────────────────────────╮
+│       Created        │    01-JAN-2011 20:53:52│
+├──────────────────────┼────────────────────────┤
+│Writer Product        │PSPP synthetic test file│
+│       Version        │1.2.3                   │
+├──────────────────────┼────────────────────────┤
+│       Compression    │None                    │
+│       Number of Cases│                       0│
+╰──────────────────────┴────────────────────────╯
+
+╭─────────┬────────────────────────╮
+│Label    │PSPP synthetic test file│
+│Variables│                       7│
+╰─────────┴────────────────────────╯
+
+╭─┬────────┬─────┬─────────────────┬─────────┬─────┬─────────┬────────────┬────────────┬──────────────╮
+│ │Position│Label│Measurement Level│   Role  │Width│Alignment│Print Format│Write Format│Missing Values│
+├─┼────────┼─────┼─────────────────┼─────────┼─────┼─────────┼────────────┼────────────┼──────────────┤
+│i│       1│     │                 │Input    │    8│Right    │F8.0        │F8.0        │              │
+│o│       2│     │                 │Target   │    8│Right    │F8.0        │F8.0        │              │
+│b│       3│     │                 │Both     │    8│Right    │F8.0        │F8.0        │              │
+│n│       4│     │                 │None     │    8│Right    │F8.0        │F8.0        │              │
+│p│       5│     │                 │Partition│    8│Right    │F8.0        │F8.0        │              │
+│s│       6│     │                 │Split    │    8│Right    │F8.0        │F8.0        │              │
+│x│       7│     │                 │Input    │    8│Right    │F8.0        │F8.0        │              │
+╰─┴────────┴─────┴─────────────────┴─────────┴─────┴─────────┴────────────┴────────────┴──────────────╯
+
+╭────────────────────────┬─────╮
+│Variable and Name       │Value│
+├────────────────────────┼─────┤
+│i                 $@Role│0    │
+├────────────────────────┼─────┤
+│o                 $@Role│1    │
+├────────────────────────┼─────┤
+│b                 $@Role│2    │
+├────────────────────────┼─────┤
+│n                 $@Role│3    │
+├────────────────────────┼─────┤
+│p                 $@Role│4    │
+├────────────────────────┼─────┤
+│s                 $@Role│5    │
+├────────────────────────┼─────┤
+│x                 $@Role│6    │
+╰────────────────────────┴─────╯
diff --git a/rust/pspp/src/sys/testdata/variable_roles.sack b/rust/pspp/src/sys/testdata/variable_roles.sack
new file mode 100644 (file)
index 0000000..8e47a88
--- /dev/null
@@ -0,0 +1,39 @@
+# File header.
+"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file";
+2; # Layout code
+7; # Nominal case size
+0; # Not compressed
+0; # Not weighted
+0; # 1 case.
+100.0; # Bias.
+"01 Jan 11"; "20:53:52"; s64 "PSPP synthetic test file";
+i8 0 *3;
+
+# Variables.
+2; 0; 0; 0; 0x050800 *2; s8 "I";
+2; 0; 0; 0; 0x050800 *2; s8 "O";
+2; 0; 0; 0; 0x050800 *2; s8 "B";
+2; 0; 0; 0; 0x050800 *2; s8 "N";
+2; 0; 0; 0; 0x050800 *2; s8 "P";
+2; 0; 0; 0; 0x050800 *2; s8 "S";
+2; 0; 0; 0; 0x050800 *2; s8 "X";
+
+# Machine integer info record.
+7; 3; 4; 8; 1; 2; 3; -1; 1; 1; ENDIAN; 1252;
+
+# Variable attributes record.
+7; 18; 1; COUNT (
+"I:$@Role('0'"; i8 10; ")";
+"/O:$@Role('1'"; i8 10; ")";
+"/B:$@Role('2'"; i8 10; ")";
+"/N:$@Role('3'"; i8 10; ")";
+"/P:$@Role('4'"; i8 10; ")";
+"/S:$@Role('5'"; i8 10; ")";
+"/X:$@Role('6'"; i8 10; ")";
+);
+
+# Character encoding record.
+7; 20; 1; 12; "windows-1252";
+
+# Dictionary termination record.
+999; 0;