Skip to content

[SPARK-58037] [PYTHON] [TESTS] Add DataFrame API golden file test framework for PySpark#57122

Open
mihailoale-db wants to merge 2 commits into
apache:masterfrom
mihailoale-db:df-golden-port
Open

[SPARK-58037] [PYTHON] [TESTS] Add DataFrame API golden file test framework for PySpark#57122
mihailoale-db wants to merge 2 commits into
apache:masterfrom
mihailoale-db:df-golden-port

Conversation

@mihailoale-db

@mihailoale-db mihailoale-db commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

This PR introduces a DataFrame API golden file test framework for PySpark, the Python/Spark Connect counterpart of the Scala SQLQueryTestSuite.

Framework (python/pyspark/testing/df_golden.py):

  • A test is described by a .test file using a golden-file grammar (--! section headers, !-- end delimiters), including a __file_metadata__ header block.
  • The .test file doubles as the golden file: expected outputs are stored inline per case (analyzed and optimized logical plans, output schema, pretty-printed result table, sha256 result hash, or expected analysis/execution error) and rewritten in place on regenerati>
  • Each case references a standalone Python script that builds the DataFrame under test (assigns df); cases run in file order against a shared session, so earlier cases can set up temp views for later ones. Each .test file runs in its own fresh Spark Connect sessio>
  • Plans are captured via explain(mode="extended") and normalized for determinism (mirroring SQLQueryTestHelper.replaceNotIncludedMsg); golden files are generated with ANSI mode on, matching the SQL golden tests.
  • Only Spark errors (PySparkException) can become expected error sections; Python bugs in case scripts fail the test instead of being captured into goldens. Error messages are stripped of JVM stacktraces, the == DataFrame == query context block, and trailing plan >
  • Malformed .test content fails loudly: unknown sections, header keys, and tags are rejected, as are cases that assert nothing and files with no cases.

Test suite (python/pyspark/sql/tests/df_golden/test_df_golden.py):

  • Uses ReusedConnectTestCase to run over Spark Connect.
  • Auto-discovers .test files and registers one test method per file; adding a new test file requires no code changes. Discovering zero files registers a failing test so a packaging problem cannot silently produce a green no-op suite.
  • regenerate.sh wraps SPARK_GENERATE_GOLDEN_FILES=1 python/run-tests, with a --verify mode that re-runs the suite against the regenerated files.

Framework unit tests (python/pyspark/sql/tests/df_golden/test_df_golden_framework.py):

  • Pure-Python unit tests for the parse / serialize / validate / normalize / render machinery, so they are fast and need no Spark session.

Initial test coverage (group_by.test, 39 cases):

  • GROUP BY / aggregate operations ported from the SQL golden tests: empty and non-empty groupBy expressions, grouping by literals, complex expressions and structs, null handling, boolean aggregates (every/some/bool_and/bool_or), HAVING filters, window expressi>

Why are the changes needed?

The SQL golden file suite (SQLQueryTestSuite) only exercises the SQL parser path. DataFrame programs constructed via the API — especially over Spark Connect, which serializes the plan and tags nodes with PLAN_ID_TAG — go through a different resolution path that th>

Does this PR introduce any user-facing change?

No. This is test-only (a new test framework plus its first test corpus); there are no production code changes.

How was this patch tested?

This PR is test-only.

  • New golden test suite: pyspark.sql.tests.df_golden.test_df_golden passes, running all 39 group_by.test cases over Spark Connect.
  • The 39 group_by.test cases were generated end to end against this Spark build via SPARK_GENERATE_GOLDEN_FILES=1 and then verified by re-running the suite without regeneration (all golden comparisons pass): analyzed/optimized plans, output schema, result tables, >
  • Framework unit tests: pyspark.sql.tests.df_golden.test_df_golden_framework (45 tests) covering .test parsing round-trip, unknown section/tag/header rejection, vacuous-case and empty-file detection, explain-section extraction, error message normalization, double >

Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Opus 4.8)

…work for PySpark

This introduces a DataFrame API golden file test framework for PySpark, the
Python/Spark Connect counterpart of the Scala SQLQueryTestSuite.

A test is described by a `.test` file (the Reyden golden file grammar: `--!
section` headers, `!-- end` delimiters) which doubles as the golden file:
expected outputs (analyzed and optimized logical plans, output schema,
pretty-printed result table, sha256 result hash, or expected error) are stored
inline per case and rewritten in place on regeneration
(`SPARK_GENERATE_GOLDEN_FILES=1`). Each case references a standalone Python
script that builds the DataFrame under test (assigns `df`); cases run in file
order against a fresh Spark Connect session per file (the connect counterpart of
SQLQueryTestSuite's per-file `newSession()`), so state cannot leak across files.

- `python/pyspark/testing/df_golden.py`: the framework (parse/serialize,
  output normalization, plan extraction, result formatting, execution engine,
  validation).
- `python/pyspark/sql/tests/df_golden/test_df_golden.py`: auto-discovers
  `.test` files over Spark Connect (ReusedConnectTestCase); one test method per
  file, no code change to add a file.
- `python/pyspark/sql/tests/df_golden/test_df_golden_framework.py`: pure-Python
  unit tests for the parse/serialize/validate/normalize/render machinery.
- `python/pyspark/sql/tests/df_golden/group_by.test` + `scripts/group_by/`:
  first corpus, 39 GROUP BY / aggregate cases ported from the SQL golden tests.
- `regenerate.sh`: wrapper that runs the module with SPARK_GENERATE_GOLDEN_FILES=1.
- Registered both test modules in `dev/sparktestsupport/modules.py`; ship the
  `.test` files and scripts in the sdist via `python/MANIFEST.in`.

This is test-only (new framework plus its first corpus; no production changes).

Co-authored-by: Isaac
The DataFrame golden test scripts under df_golden/scripts are executed by
the framework with `spark` injected into their namespace, so `spark` is
undefined at lint time and ruff reports F821. Add a per-file-ignores entry
scoped to that scripts directory.

Co-authored-by: Isaac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant