diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index 68dcb1049b8..96a67c510d0 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -13,15 +13,10 @@ jobs: with: fetch-depth: 2 - - name: Install APT dependencies + # This needs to happen before Python and npm execution; it must happen before any extra files are written. + - name: .gitignore checks (gitignore_check.sh) run: | - sudo apt update - sudo apt install -y libxml2-utils - - - name: Install Python dependencies and general setup - run: | - pip3 install pytest==7.1.2 - git config diff.wsErrorHighlight all + bash ./misc/scripts/gitignore_check.sh - name: Get changed files env: @@ -32,27 +27,17 @@ jobs: elif [ "${{ github.event_name }}" == "push" -a "${{ github.event.forced }}" == "false" -a "${{ github.event.created }}" == "false" ]; then files=$(git diff-tree --no-commit-id --name-only -r ${{ github.event.before }}..${{ github.event.after }} 2> /dev/null || true) fi - echo "$files" >> changed.txt - cat changed.txt - files=$(echo "$files" | grep -v 'thirdparty' | xargs -I {} sh -c 'echo "\"./{}\""' | tr '\n' ' ') + files=$(echo "$files" | xargs -I {} sh -c 'echo "\"./{}\""' | tr '\n' ' ') echo "CHANGED_FILES=$files" >> $GITHUB_ENV - # This needs to happen before Python and npm execution; it must happen before any extra files are written. - - name: .gitignore checks (gitignore_check.sh) - run: | - bash ./misc/scripts/gitignore_check.sh - - name: Style checks via pre-commit uses: pre-commit/action@v3.0.1 with: extra_args: --files ${{ env.CHANGED_FILES }} - - name: Python builders checks via pytest - run: | - pytest ./tests/python_build - - name: Class reference schema checks run: | + sudo apt install -y libxml2-utils xmllint --quiet --noout --schema doc/class.xsd doc/classes/*.xml modules/*/doc_classes/*.xml platform/*/doc_classes/*.xml - name: Run C compiler on `gdextension_interface.h` diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 21a71985eb1..4b79118f71c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,12 +16,10 @@ repos: - id: clang-format files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$ types_or: [text] - exclude: ^tests/python_build/.* - id: clang-format name: clang-format-glsl files: \.glsl$ types_or: [text] - exclude: ^tests/python_build/.* args: [-style=file:misc/utility/clang_format_glsl.yml] - repo: https://github.com/pocc/pre-commit-hooks @@ -31,7 +29,6 @@ repos: files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$ args: [--fix, --quiet, --use-color] types_or: [text] - exclude: ^tests/python_build/.* additional_dependencies: [clang-tidy==19.1.0] require_serial: true stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy` @@ -48,7 +45,7 @@ repos: types_or: [text] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.1 # Latest version that supports Python 3.8 + rev: v1.14.1 # Latest version that supports Python 3.8 hooks: - id: mypy files: \.py$ @@ -88,6 +85,13 @@ repos: pass_filenames: false files: ^(doc/classes|.*/doc_classes)/.*\.xml$ + - id: validate-builders + name: validate-builders + language: python + entry: python tests/python_build/validate_builders.py + pass_filenames: false + files: ^(gles3|glsl)_builders\.py$ + - id: eslint name: eslint language: node diff --git a/tests/python_build/conftest.py b/tests/python_build/conftest.py deleted file mode 100644 index 617230926a2..00000000000 --- a/tests/python_build/conftest.py +++ /dev/null @@ -1,26 +0,0 @@ -import os -import sys -from pathlib import Path - -import pytest - -CWD = Path(__file__).parent -ROOT = CWD.parent.parent -# append directory with build files to sys.path to import them -sys.path.append(str(ROOT)) - - -@pytest.fixture -def shader_files(request): - shader_path = request.param - - res = { - "path_input": str(CWD / "fixtures" / f"{shader_path}.glsl"), - "path_output": str(CWD / "fixtures" / f"{shader_path}.glsl.gen.h"), - "path_expected_full": str(CWD / "fixtures" / f"{shader_path}_expected_full.glsl"), - "path_expected_parts": str(CWD / "fixtures" / f"{shader_path}_expected_parts.json"), - } - yield res - - if not os.getenv("PYTEST_KEEP_GENERATED_FILES"): - os.remove(res["path_output"]) diff --git a/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl b/tests/python_build/fixtures/gles3/vertex_fragment.out similarity index 100% rename from tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl rename to tests/python_build/fixtures/gles3/vertex_fragment.out diff --git a/tests/python_build/fixtures/gles3/vertex_fragment_expected_parts.json b/tests/python_build/fixtures/gles3/vertex_fragment_expected_parts.json deleted file mode 100644 index 5ac8092ad0f..00000000000 --- a/tests/python_build/fixtures/gles3/vertex_fragment_expected_parts.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "vertex_lines": [ - "", - "precision highp float;", - "precision highp int;", - "", - "layout(location = 0) in highp vec3 vertex;", - "", - "out highp vec4 position_interp;", - "", - "void main() {", - "\tposition_interp = vec4(vertex.x,1,0,1);", - "}", - "" - ], - "fragment_lines": [ - "", - "precision highp float;", - "precision highp int;", - "", - "in highp vec4 position_interp;", - "", - "void main() {", - "\thighp float depth = ((position_interp.z / position_interp.w) + 1.0);", - "\tfrag_color = vec4(depth);", - "}" - ], - "uniforms": [], - "fbos": [], - "texunits": [], - "texunit_names": [], - "ubos": [], - "ubo_names": [], - "feedbacks": [], - "vertex_included_files": [], - "fragment_included_files": [], - "reading": "fragment", - "line_offset": 33, - "vertex_offset": 10, - "fragment_offset": 23, - "variant_defines": [ - "#define USE_NINEPATCH" - ], - "variant_names": [ - "MODE_NINEPATCH" - ], - "specialization_names": [ - "DISABLE_LIGHTING" - ], - "specialization_values": [ - " false\n" - ] -} diff --git a/tests/python_build/fixtures/glsl/compute_expected_full.glsl b/tests/python_build/fixtures/glsl/compute.out similarity index 100% rename from tests/python_build/fixtures/glsl/compute_expected_full.glsl rename to tests/python_build/fixtures/glsl/compute.out diff --git a/tests/python_build/fixtures/glsl/compute_expected_parts.json b/tests/python_build/fixtures/glsl/compute_expected_parts.json deleted file mode 100644 index 025c568ae08..00000000000 --- a/tests/python_build/fixtures/glsl/compute_expected_parts.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "code": "#[compute]\n\n#version 450\n\n#VERSION_DEFINES\n\n\n#define M_PI 3.14159265359\n\nvoid main() {\n\tvec3 static_light = vec3(0, 1, 0);\n}\n" -} diff --git a/tests/python_build/fixtures/glsl/vertex_fragment_expected_full.glsl b/tests/python_build/fixtures/glsl/vertex_fragment.out similarity index 100% rename from tests/python_build/fixtures/glsl/vertex_fragment_expected_full.glsl rename to tests/python_build/fixtures/glsl/vertex_fragment.out diff --git a/tests/python_build/fixtures/glsl/vertex_fragment_expected_parts.json b/tests/python_build/fixtures/glsl/vertex_fragment_expected_parts.json deleted file mode 100644 index 38312367a09..00000000000 --- a/tests/python_build/fixtures/glsl/vertex_fragment_expected_parts.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "code": "#[versions]\n\nlines = \"#define MODE_LINES\";\n\n#[vertex]\n\n#version 450\n\n#VERSION_DEFINES\n\nlayout(location = 0) out vec3 uv_interp;\n\nvoid main() {\n\n#ifdef MODE_LINES\n\tuv_interp = vec3(0,0,1);\n#endif\n}\n\n#[fragment]\n\n#version 450\n\n#VERSION_DEFINES\n\n#define M_PI 3.14159265359\n\nlayout(location = 0) out vec4 dst_color;\n\nvoid main() {\n\tdst_color = vec4(1,1,0,0);\n}\n" -} diff --git a/tests/python_build/fixtures/rd_glsl/compute_expected_full.glsl b/tests/python_build/fixtures/rd_glsl/compute.out similarity index 100% rename from tests/python_build/fixtures/rd_glsl/compute_expected_full.glsl rename to tests/python_build/fixtures/rd_glsl/compute.out diff --git a/tests/python_build/fixtures/rd_glsl/compute_expected_parts.json b/tests/python_build/fixtures/rd_glsl/compute_expected_parts.json deleted file mode 100644 index 26ba9e4fc42..00000000000 --- a/tests/python_build/fixtures/rd_glsl/compute_expected_parts.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "vertex_lines": [], - "fragment_lines": [], - "compute_lines": [ - "", - "#version 450", - "", - "#VERSION_DEFINES", - "", - "#define BLOCK_SIZE 8", - "", - "#define M_PI 3.14159265359", - "", - "void main() {", - "\tuint t = BLOCK_SIZE + 1;", - "}" - ], - "vertex_included_files": [], - "fragment_included_files": [], - "compute_included_files": [ - "tests/python_build/fixtures/rd_glsl/_included.glsl" - ], - "reading": "compute", - "line_offset": 13, - "vertex_offset": 0, - "fragment_offset": 0, - "compute_offset": 1 -} diff --git a/tests/python_build/fixtures/rd_glsl/vertex_fragment_expected_full.glsl b/tests/python_build/fixtures/rd_glsl/vertex_fragment.out similarity index 100% rename from tests/python_build/fixtures/rd_glsl/vertex_fragment_expected_full.glsl rename to tests/python_build/fixtures/rd_glsl/vertex_fragment.out diff --git a/tests/python_build/fixtures/rd_glsl/vertex_fragment_expected_parts.json b/tests/python_build/fixtures/rd_glsl/vertex_fragment_expected_parts.json deleted file mode 100644 index dbf833edead..00000000000 --- a/tests/python_build/fixtures/rd_glsl/vertex_fragment_expected_parts.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "vertex_lines": [ - "", - "#version 450", - "", - "#VERSION_DEFINES", - "", - "#define M_PI 3.14159265359", - "", - "layout(location = 0) out vec2 uv_interp;", - "", - "void main() {", - "\tuv_interp = vec2(0, 1);", - "}", - "" - ], - "fragment_lines": [ - "", - "#version 450", - "", - "#VERSION_DEFINES", - "", - "layout(location = 0) in vec2 uv_interp;", - "", - "void main() {", - "\tuv_interp = vec2(1, 0);", - "}" - ], - "compute_lines": [], - "vertex_included_files": [ - "tests/python_build/fixtures/rd_glsl/_included.glsl" - ], - "fragment_included_files": [], - "compute_included_files": [], - "reading": "fragment", - "line_offset": 25, - "vertex_offset": 1, - "fragment_offset": 15, - "compute_offset": 0 -} diff --git a/tests/python_build/test_gles3_builder.py b/tests/python_build/test_gles3_builder.py deleted file mode 100644 index b34d33bde7e..00000000000 --- a/tests/python_build/test_gles3_builder.py +++ /dev/null @@ -1,31 +0,0 @@ -import json - -import pytest - -from gles3_builders import GLES3HeaderStruct, build_gles3_header - - -@pytest.mark.parametrize( - ["shader_files", "builder", "header_struct"], - [ - ("gles3/vertex_fragment", build_gles3_header, GLES3HeaderStruct), - ], - indirect=["shader_files"], -) -def test_gles3_builder(shader_files, builder, header_struct): - header = header_struct() - - builder(shader_files["path_input"], "drivers/gles3/shader_gles3.h", "GLES3", header_data=header) - - with open(shader_files["path_expected_parts"], "r", encoding="utf-8") as f: - expected_parts = json.load(f) - assert expected_parts == header.__dict__ - - with open(shader_files["path_output"], "r", encoding="utf-8") as f: - actual_output = f.read() - assert actual_output - - with open(shader_files["path_expected_full"], "r", encoding="utf-8") as f: - expected_output = f.read() - - assert actual_output == expected_output diff --git a/tests/python_build/test_glsl_builder.py b/tests/python_build/test_glsl_builder.py deleted file mode 100644 index 9f548855ffc..00000000000 --- a/tests/python_build/test_glsl_builder.py +++ /dev/null @@ -1,37 +0,0 @@ -import json - -import pytest - -from glsl_builders import RAWHeaderStruct, RDHeaderStruct, build_raw_header, build_rd_header - - -@pytest.mark.parametrize( - [ - "shader_files", - "builder", - "header_struct", - ], - [ - ("glsl/vertex_fragment", build_raw_header, RAWHeaderStruct), - ("glsl/compute", build_raw_header, RAWHeaderStruct), - ("rd_glsl/vertex_fragment", build_rd_header, RDHeaderStruct), - ("rd_glsl/compute", build_rd_header, RDHeaderStruct), - ], - indirect=["shader_files"], -) -def test_glsl_builder(shader_files, builder, header_struct): - header = header_struct() - builder(shader_files["path_input"], header_data=header) - - with open(shader_files["path_expected_parts"], "r", encoding="utf-8") as f: - expected_parts = json.load(f) - assert expected_parts == header.__dict__ - - with open(shader_files["path_output"], "r", encoding="utf-8") as f: - actual_output = f.read() - assert actual_output - - with open(shader_files["path_expected_full"], "r", encoding="utf-8") as f: - expected_output = f.read() - - assert actual_output == expected_output diff --git a/tests/python_build/validate_builders.py b/tests/python_build/validate_builders.py new file mode 100644 index 00000000000..1375c448c7a --- /dev/null +++ b/tests/python_build/validate_builders.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +if __name__ != "__main__": + raise ImportError(f"{__name__} should not be used as a module.") + +import os +import sys +from typing import Any, Callable + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) + +from gles3_builders import build_gles3_header +from glsl_builders import build_raw_header, build_rd_header + +FUNC_PATH_KWARGS: list[tuple[Callable[..., None], str, dict[str, Any]]] = [ + ( + build_gles3_header, + "tests/python_build/fixtures/gles3/vertex_fragment.out", + {"shader": "tests/python_build/fixtures/gles3/vertex_fragment.glsl"}, + ), + ( + build_raw_header, + "tests/python_build/fixtures/glsl/compute.out", + {"shader": "tests/python_build/fixtures/glsl/compute.glsl"}, + ), + ( + build_raw_header, + "tests/python_build/fixtures/glsl/vertex_fragment.out", + {"shader": "tests/python_build/fixtures/glsl/vertex_fragment.glsl"}, + ), + ( + build_rd_header, + "tests/python_build/fixtures/rd_glsl/compute.out", + {"shader": "tests/python_build/fixtures/rd_glsl/compute.glsl"}, + ), + ( + build_rd_header, + "tests/python_build/fixtures/rd_glsl/vertex_fragment.out", + {"shader": "tests/python_build/fixtures/rd_glsl/vertex_fragment.glsl"}, + ), +] + + +def main() -> int: + ret = 0 + + for func, path, kwargs in FUNC_PATH_KWARGS: + if os.path.exists(out_path := os.path.abspath(path)): + with open(out_path, "rb") as file: + raw = file.read() + func(path, **kwargs) + with open(out_path, "rb") as file: + if raw != file.read(): + ret += 1 + else: + func(path, **kwargs) + ret += 1 + + return ret + + +sys.exit(main())