Skip to content

Commit 51e41d5

Browse files
committed
test: Run integration tests across Cinema 4D versions (2024,2025,2026)
Signed-off-by: Karthik Bekal Pattathana <133984042+karthikbekalp@users.noreply.github.com>
1 parent aa148a6 commit 51e41d5

231 files changed

Lines changed: 226 additions & 62 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/integ_macos.yml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ name: "Integ Tests - macOS"
22

33
on:
44
workflow_call:
5+
inputs:
6+
c4d_version:
7+
description: Cinema 4D major version to test
8+
required: true
9+
type: string
510
secrets:
611
AWS_OIDC_ROLE_ARN:
712
required: true
@@ -22,8 +27,9 @@ jobs:
2227
IntegMacOS:
2328
if: >-
2429
github.repository == 'aws-deadline/deadline-cloud-for-cinema-4d' &&
25-
github.ref == 'refs/heads/mainline'
26-
name: Integration Tests (macOS)
30+
(github.ref == 'refs/heads/mainline' ||
31+
github.ref == 'refs/heads/feature/ci-tests')
32+
name: Integration Tests (macOS, Cinema 4D ${{ inputs.c4d_version }})
2733
runs-on: macos-latest
2834
timeout-minutes: 60
2935
permissions:
@@ -35,7 +41,7 @@ jobs:
3541
redshift_LICENSE: "${{ secrets.RLM_PORT }}@127.0.0.1"
3642
g_licenseServerRLM: "127.0.0.1:${{ secrets.RLM_PORT }}"
3743
g_licenseModel: "LICENSEMODEL::RLM"
38-
C4D_VERSION: "2026"
44+
C4D_VERSION: ${{ inputs.c4d_version }}
3945
INSTALLER_BUCKET: ${{ secrets.INSTALLER_BUCKET }}
4046
INSTALLER_BUCKET_EXPECTED_OWNER: ${{ secrets.INSTALLER_BUCKET_EXPECTED_OWNER }}
4147
steps:
@@ -82,8 +88,8 @@ jobs:
8288
8389
- name: Grant accessibility to Cinema 4D
8490
run: |
85-
C4D_BIN="/Applications/Maxon Cinema 4D 2026/Cinema 4D.app/Contents/MacOS/Cinema 4D"
86-
C4DPY_BIN="/Applications/Maxon Cinema 4D 2026/c4dpy.app/Contents/MacOS/c4dpy"
91+
C4D_BIN="/Applications/Maxon Cinema 4D $C4D_VERSION/Cinema 4D.app/Contents/MacOS/Cinema 4D"
92+
C4DPY_BIN="/Applications/Maxon Cinema 4D $C4D_VERSION/c4dpy.app/Contents/MacOS/c4dpy"
8793
for BIN_PATH in "$C4D_BIN" "$C4DPY_BIN"; do
8894
if [ -f "$BIN_PATH" ]; then
8995
sudo sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" \

.github/workflows/integ_windows.yml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ name: "Integ Tests - Windows"
22

33
on:
44
workflow_call:
5+
inputs:
6+
c4d_version:
7+
description: Cinema 4D major version to test
8+
required: true
9+
type: string
510
secrets:
611
AWS_OIDC_ROLE_ARN:
712
required: true
@@ -22,8 +27,9 @@ jobs:
2227
IntegWindows:
2328
if: >-
2429
github.repository == 'aws-deadline/deadline-cloud-for-cinema-4d' &&
25-
github.ref == 'refs/heads/mainline'
26-
name: Integration Tests (Windows)
30+
(github.ref == 'refs/heads/mainline' ||
31+
github.ref == 'refs/heads/feature/ci-tests')
32+
name: Integration Tests (Windows, Cinema 4D ${{ inputs.c4d_version }})
2733
runs-on: windows-latest
2834
timeout-minutes: 60
2935
permissions:
@@ -35,7 +41,7 @@ jobs:
3541
redshift_LICENSE: "${{ secrets.RLM_PORT }}@127.0.0.1"
3642
g_licenseServerRLM: "127.0.0.1:${{ secrets.RLM_PORT }}"
3743
g_licenseModel: "LICENSEMODEL::RLM"
38-
C4D_VERSION: "2026"
44+
C4D_VERSION: ${{ inputs.c4d_version }}
3945
INSTALLER_BUCKET: ${{ secrets.INSTALLER_BUCKET }}
4046
INSTALLER_BUCKET_EXPECTED_OWNER: ${{ secrets.INSTALLER_BUCKET_EXPECTED_OWNER }}
4147
steps:
@@ -135,6 +141,15 @@ jobs:
135141
136142
hatch run integ:test
137143
144+
- name: Upload render outputs
145+
if: always()
146+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
147+
with:
148+
name: cinema4d-${{ inputs.c4d_version }}-render-outputs
149+
path: test/integ/test_cases/**/actual/renders/**
150+
if-no-files-found: ignore
151+
retention-days: 7
152+
138153
- name: Tear down
139154
if: always()
140155
shell: pwsh

.github/workflows/integration_tests_xa11y.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@ on:
44
push:
55
branches:
66
- mainline
7+
- feature/ci-tests
78

89
jobs:
910
windows:
11+
name: Windows, Cinema 4D ${{ matrix.c4d_version }}
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
c4d_version: ["2024", "2025", "2026"]
1016
uses: ./.github/workflows/integ_windows.yml
17+
with:
18+
c4d_version: ${{ matrix.c4d_version }}
1119
secrets:
1220
AWS_OIDC_ROLE_ARN: ${{ secrets.AWS_OIDC_ROLE_ARN }}
1321
AWS_REGION: ${{ secrets.AWS_REGION }}
@@ -21,7 +29,14 @@ jobs:
2129
contents: read
2230

2331
macos:
32+
name: macOS, Cinema 4D ${{ matrix.c4d_version }}
33+
strategy:
34+
fail-fast: false
35+
matrix:
36+
c4d_version: ["2024", "2025", "2026"]
2437
uses: ./.github/workflows/integ_macos.yml
38+
with:
39+
c4d_version: ${{ matrix.c4d_version }}
2540
secrets:
2641
AWS_OIDC_ROLE_ARN: ${{ secrets.AWS_OIDC_ROLE_ARN }}
2742
AWS_REGION: ${{ secrets.AWS_REGION }}

pipeline/setup-runner.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,23 @@
1414
from botocore.config import Config
1515

1616
C4D_INSTALLERS = {
17+
"2024": {
18+
"windows": {
19+
"s3_key": "cinema4d/2024/Cinema4D_2024_2024.5.1_Win.exe",
20+
"sha256": "e71f7f103bb783fc5155e1b43e057d20c839052a0fe712ea3f42e447acff46b1",
21+
"type": "exe",
22+
},
23+
"macos": {
24+
"s3_key": "cinema4d/2024/Cinema4D_2024_2024.5.1_Mac.dmg",
25+
"sha256": "08d3d0efc8dd06f69fae8f86bf50539689b12029a2ac79c0d6c358f7d9d63edc",
26+
"type": "dmg",
27+
},
28+
},
1729
"2025": {
1830
"windows": {
19-
"s3_key": "cinema4d/2025/Cinema4D_2025_2025.3.3_Win.zip",
20-
"sha256": "fcf0ea40af73727f1bc1fb1a47ea0c2f3476f0652824d3b740181dc1a6123e09",
21-
"type": "zip",
31+
"s3_key": "cinema4d/2025/Cinema4D_2025_2025.3.3_Win.exe",
32+
"sha256": "66f5f5b74f93b32490d32386c642d6857aef98629a5b3eaa88fa366b1598a18e",
33+
"type": "exe",
2234
},
2335
"macos": {
2436
"s3_key": "cinema4d/2025/Cinema4D_2025_2025.3.3_Mac.dmg",
@@ -41,6 +53,10 @@
4153
}
4254

4355
C4D_INSTALL_PATHS = {
56+
"2024": {
57+
"windows": Path("C:/Program Files/Maxon Cinema 4D 2024"),
58+
"macos": Path("/Applications/Maxon Cinema 4D 2024"),
59+
},
4460
"2025": {
4561
"windows": Path("C:/Program Files/Maxon Cinema 4D 2025"),
4662
"macos": Path("/Applications/Maxon Cinema 4D 2025"),
@@ -182,17 +198,17 @@ def setup_windows(versions):
182198
def setup_macos(versions):
183199
"""Install Cinema 4D on macOS for each version."""
184200
for version in versions:
201+
if "macos" not in C4D_INSTALLERS.get(version, {}):
202+
print(f"ERROR: No macOS installer configured for version {version}")
203+
sys.exit(1)
204+
185205
install_dir = C4D_INSTALL_PATHS[version]["macos"]
186206
marker = install_dir / ".installed"
187207

188208
if marker.exists():
189209
print(f"Cinema 4D {version} already installed at {install_dir}")
190210
continue
191211

192-
if "macos" not in C4D_INSTALLERS.get(version, {}):
193-
print(f"ERROR: No macOS installer configured for version {version}")
194-
sys.exit(1)
195-
196212
print(f"Installing Cinema 4D {version}...")
197213
installer_info = C4D_INSTALLERS[version]["macos"]
198214
local_installer = Path(f"/tmp/{Path(installer_info['s3_key']).name}")
@@ -333,7 +349,7 @@ def _configure_rlm_licensing(versions, platform_key):
333349
"--versions",
334350
nargs="+",
335351
required=True,
336-
help="Cinema 4D versions to install (e.g., 2025 2026)",
352+
help="Cinema 4D versions to install (e.g., 2024 2025 2026)",
337353
)
338354
args = parser.parse_args()
339355

skills/c4d-dev/references/integration-testing.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ purpose of this suite is to cover the customer-visible plugin and UI path.
2626
- Run Windows tests in an interactive desktop session; UI Automation does not
2727
work in Session 0.
2828
- Configure renderer licensing for renderer-specific cases.
29-
- Set `C4D_LOCATION` when Cinema 4D is outside a known default path.
29+
- Local runs default to Cinema 4D 2026. Set `C4D_VERSION` to run an older
30+
version, and set `C4D_LOCATION` when that version is outside its default path.
3031

3132
```powershell
32-
$env:C4D_LOCATION = "C:\Program Files\Maxon Cinema 4D 2026\"
33+
$env:C4D_VERSION = "2025"
34+
$env:C4D_LOCATION = "D:\Applications\Maxon Cinema 4D 2025\"
3335
$env:redshift_LICENSE = "<port>@<license-server>"
3436
```
3537

@@ -48,6 +50,7 @@ test/integ/test_cases/<case>/
4850
└── expected/
4951
├── job_bundle/
5052
└── renders/
53+
└── <C4D_VERSION>/
5154
```
5255

5356
Use a dedicated parametrized test when one scene and configurator intentionally
@@ -128,13 +131,29 @@ After the missing-golden failure leaves output in `actual/`:
128131
ignores it.
129132
5. Serialize all three documents as block YAML.
130133
6. On Windows, copy the reviewed files from `actual/renders/` into the matching
131-
`expected/renders/` directory.
134+
`expected/renders/<C4D_VERSION>/` directory.
132135

133136
Use structured YAML operations rather than text substitutions. Review every
134137
golden diff before accepting it; generated output is not automatically correct.
135138

136139
Never commit `actual/`.
137140

141+
## Capture Render Outputs in CI
142+
143+
When local Windows rendering is not practical:
144+
145+
1. Push the changes to the repository's `feature/ci-tests` branch.
146+
2. Wait for the **Cinema 4D xa11y Integration Tests** workflow to finish.
147+
3. Download the `cinema4d-<C4D_VERSION>-render-outputs` artifact from the run.
148+
4. Review the extracted `<case>/actual/renders/` images.
149+
5. Copy approved PNGs into the matching
150+
`expected/renders/<C4D_VERSION>/` directory, or the corresponding
151+
`expected/<variant>/renders/<C4D_VERSION>/` directory.
152+
153+
Only retained `actual/` files are uploaded. Successful cases clean that
154+
directory, while missing or mismatched goldens leave output available for
155+
download. Never commit the extracted `actual/` directories.
156+
138157
## Verify
139158

140159
Run focused checks first, then the full applicable suite:

src/deadline/cinema4d_adaptor/Cinema4DClient/plugin/DeadlineCloudClient.pyp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ print('system paths:')
88
for n in sys.path:
99
print(n)
1010

11+
_adaptor_testing = os.environ.get("CINEMA4D_ADAPTOR_TESTING", "false").lower() == "true"
12+
13+
# Integration tests inject pywin32 from a Hatch environment without Conda activation.
14+
# Keep this handle alive so win32file can load pywintypes311.dll in that test environment.
15+
_dll_directory_handles = []
16+
if sys.platform == "win32" and _adaptor_testing:
17+
for python_path in sys.path:
18+
pywin32_dll_directory = os.path.join(python_path, "pywin32_system32")
19+
if os.path.isdir(pywin32_dll_directory):
20+
_dll_directory_handles.append(os.add_dll_directory(pywin32_dll_directory))
21+
1122
import c4d
1223

1324
# The Cinema4D Adaptor adds the `deadline` namespace directory to PYTHONPATH,
@@ -33,5 +44,5 @@ def PluginMessage(id, data):
3344
# TODO: Investigate if this file is still needed.
3445
# Currently, PluginMessage function is not called when the plugin is loaded in adaptor tests.
3546
# To unblock for tests, we check if the environment variable is set and if so, run the main function.
36-
if os.environ.get("CINEMA4D_ADAPTOR_TESTING", "false").lower() == "true":
37-
main()
47+
if _adaptor_testing:
48+
main()

test/AGENTS.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ test_cases/<name>/
118118
│ # before Export (runs in pytest + xa11y)
119119
├── expected/ # what we compare against
120120
│ ├── job_bundle/ # golden template/parameter_values/asset_references
121-
│ └── renders/ # golden render PNGs (Windows-only compare)
121+
│ └── renders/ # version-specific golden PNGs (Windows-only compare)
122+
│ └── <C4D_VERSION>/ # required baseline for each tested C4D version
122123
└── actual/ # gitignored — runtime output; kept on failure
123124
```
124125

@@ -134,7 +135,29 @@ need per-farm regeneration.
134135
`input/scene.py` runs inside **c4dpy** (Cinema 4D's headless Python) and is saved
135136
into `actual/` (not `input/`), so render paths under `renders/` resolve into
136137
`actual/renders/`. Cases may override the output filename, but keep this
137-
directory stable for render comparison.
138+
directory stable for render comparison. Render comparison requires a baseline
139+
under `expected/renders/<C4D_VERSION>/`. Failed Windows CI cases
140+
retain their generated images and upload them in the
141+
`cinema4d-<version>-render-outputs` workflow artifact for seven days.
142+
143+
#### Downloading render outputs from CI
144+
145+
Use the versioned Windows CI jobs when render goldens cannot be generated
146+
locally:
147+
148+
1. Push the changes to the `feature/ci-tests` branch in this repository.
149+
2. Wait for the **Cinema 4D xa11y Integration Tests** workflow to finish.
150+
3. Open the workflow run and download the
151+
`cinema4d-<C4D_VERSION>-render-outputs` artifact for each required version.
152+
4. Extract the artifact and review every PNG under
153+
`<case>/actual/renders/`.
154+
5. Copy approved images into
155+
`test/integ/test_cases/<case>/expected/renders/<C4D_VERSION>/`, or
156+
`expected/<variant>/renders/<C4D_VERSION>/` for a parametrized case.
157+
158+
The artifact step always runs, but successful cases remove their `actual/`
159+
directory before upload. Missing or mismatched goldens leave render output
160+
behind for collection. Never commit the extracted `actual/` directories.
138161

139162
#### Focused settings coverage
140163

@@ -252,6 +275,11 @@ production.
252275
hatch run integ:test # all xa11y integ tests
253276
```
254277

278+
CI runs this suite against Cinema 4D 2024, 2025, and 2026 on Windows and
279+
macOS. All six jobs run in parallel. Local runs default to Cinema 4D 2026.
280+
Set `C4D_VERSION` for an older version and set `C4D_LOCATION` only when its
281+
installation is outside that version's default path.
282+
255283
The `integ:test` script hardcodes the `test/integ` path and
256284
`--numprocesses=1`. Beware: any args you pass *replace* the path (hatch
257285
`{args:test/integ}` falls back to the global `testpaths = ["test"]`), so

0 commit comments

Comments
 (0)