Skip to content

Commit 0a0ff86

Browse files
committed
Test very long SPSS strings
1 parent 1b937aa commit 0a0ff86

3 files changed

Lines changed: 31 additions & 1 deletion

File tree

docs/pyspssio-spike.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ that require explicit tests or upstream work include:
2929
1. variable-set round trips;
3030
2. independent print and write formats;
3131
3. ordered document text;
32-
4. legacy code-page files and long-string edge cases.
32+
4. legacy code-page files and unusual very-long-string boundaries beyond the covered 340-byte UTF-8 fixture.
3333

3434
Until a feature is proved, the adapter must report it as unsupported rather
3535
than silently losing it.

docs/sav-profile.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ of this boundary. It records the pinned source commit and installed engine versi
3737
| SPSS semantic | Pinned pyspssio status | Behaviour |
3838
| --- | --- | --- |
3939
| File label | Supported through the pinned fork | The adapter persists it in the dataset catalog and writes it through the IBM I/O identifier-string API. |
40+
| Very-long UTF-8 strings | Supported | The SAV and ZSAV fixture preserves a 340-byte, multi-byte UTF-8 string through the SQL catalog and back. |
4041
| Ordered document text | Unobservable | The engine exposes file-to-file document copying, but not reading or creating document text. Import records documents-unobservable; export requires that audited loss to be accepted. |
4142
| Print and write formats independently | Supported as raw IBM I/O tuples | The adapter stores both tuples separately and writes them without collapsing either value. |
4243
| Variable sets | Supported through raw IBM I/O | The adapter stores source sets in the extension catalog and writes them through the raw dictionary setter. Invalid target definitions fail before data are written. |

tests/test_sav_sqlite.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,32 @@ def test_raw_dictionary_bridge_preserves_distinct_formats_sets_and_attribute_arr
179179
assert attribute_values(variable_attribute_pairs(reader, "answer")) == {
180180
"Array": ["red", "blue"],
181181
}
182+
183+
184+
@pytest.mark.parametrize("suffix", [".sav", ".zsav"])
185+
def test_very_long_string_round_trips_through_sqlite_and_export(tmp_path, suffix: str) -> None:
186+
payload = "ü" * 170
187+
payload_width = len(payload.encode("utf-8"))
188+
source = tmp_path / f"long-source{suffix}"
189+
destination = tmp_path / f"long-destination{suffix}"
190+
database_path = tmp_path / f"long-{suffix[1:]}.sqlite"
191+
database = f"sqlite:///{database_path}"
192+
pyspssio.write_sav(
193+
str(source), pd.DataFrame({"comment": [payload, "short"]}),
194+
)
195+
assert pyspssio.read_metadata(str(source))["var_types"]["comment"] == payload_width
196+
197+
openstatspec.import_sav(source, database_url=database, dataset_id="long")
198+
connection = sqlite3.connect(database_path)
199+
assert connection.execute(
200+
"select string_width from variable_catalog where dataset_id = ? and source_name = ?",
201+
("long", "comment"),
202+
).fetchone() == (payload_width,)
203+
204+
openstatspec.export_sav(
205+
database_url=database, dataset_id="long", destination=destination,
206+
allow_loss=_REQUIRED_ENGINE_LOSS,
207+
)
208+
frame, metadata = pyspssio.read_sav(str(destination), convert_datetimes=False)
209+
assert metadata["var_types"]["comment"] == payload_width
210+
assert frame["comment"].tolist() == [payload, "short"]

0 commit comments

Comments
 (0)