From e6d3b8d7dcf6638986911d37f556cb12bf37d8b8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 28 Jul 2026 17:15:37 +0530 Subject: [PATCH] test for compiling custom shader to glsl --- kitty/shaders/slang.py | 12 ++++++--- kitty_tests/slang.py | 56 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/kitty/shaders/slang.py b/kitty/shaders/slang.py index ab64aa3ac..acc4bb276 100644 --- a/kitty/shaders/slang.py +++ b/kitty/shaders/slang.py @@ -1092,10 +1092,14 @@ def build_custom_shader_pipeline_glsl( assert v.stderr is not None and f.stderr is not None v.stdin.write(src), v.stdin.close() f.stdin.write(src), f.stdin.close() - if (rc := v.wait()) != 0: - raise SlangFailed(f'{slot}.vert.glsl', subprocess.CompletedProcess(vcmd, rc, stderr=v.stderr.read())) - if (rc := f.wait()) != 0: - raise SlangFailed(f'{slot}.frag.glsl', subprocess.CompletedProcess(fcmd, rc, stderr=f.stderr.read())) + try: + if (rc := v.wait()) != 0: + raise SlangFailed(f'{slot}.vert.glsl', subprocess.CompletedProcess(vcmd, rc, stderr=v.stderr.read())) + if (rc := f.wait()) != 0: + raise SlangFailed(f'{slot}.frag.glsl', subprocess.CompletedProcess(fcmd, rc, stderr=f.stderr.read())) + finally: + v.stderr.close() + f.stderr.close() fixup_opengl_files((fragment, vertex)) with open(vertex) as vf, open(fragment) as ff: return vf.read(), ff.read(), glsl_metadata_for_shader(metadata) diff --git a/kitty_tests/slang.py b/kitty_tests/slang.py index 97c6806a9..7e9eb27b3 100644 --- a/kitty_tests/slang.py +++ b/kitty_tests/slang.py @@ -2,9 +2,21 @@ # License: GPLv3 Copyright: 2026, Kovid Goyal import os +import shutil +import subprocess import tempfile -from kitty.shaders.slang import EntryPoint, SlangFile, Stage, build_import_graph, parse_slang_text, topological_layers, topological_sort +from kitty.constants import slangc +from kitty.shaders.slang import ( + EntryPoint, + SlangFile, + Stage, + build_custom_shader_pipeline_glsl, + build_import_graph, + parse_slang_text, + topological_layers, + topological_sort, +) from .base import BaseTest @@ -228,3 +240,45 @@ void vsMain() {} # Empty graph self.assertEqual(topological_layers({}), []) + + def test_build_custom_shader_pipeline_glsl(self): + if not shutil.which(slangc()[0]): + self.skipTest(f'slangc ({slangc()[0]}) not found in PATH') + + with tempfile.TemporaryDirectory() as cache_dir: + # Clear the lru_cache so the temp cache_dir is actually used + build_custom_shader_pipeline_glsl.cache_clear() + try: + vert_src, frag_src, metadata = build_custom_shader_pipeline_glsl( + slot='after-window-background', + shaders=('sample', 'sample'), + cache_dir=cache_dir, + ) + finally: + build_custom_shader_pipeline_glsl.cache_clear() + + self.assertIsInstance(vert_src, str) + self.assertIsInstance(frag_src, str) + self.assertTrue(len(vert_src) > 0, 'vertex GLSL is empty') + self.assertTrue(len(frag_src) > 0, 'fragment GLSL is empty') + self.assertIsInstance(metadata, dict) + + if not shutil.which('glslangValidator'): + return + + for src, stage, ext in ((vert_src, 'vert', '.vert.glsl'), (frag_src, 'frag', '.frag.glsl')): + with tempfile.NamedTemporaryFile(suffix=ext, mode='w', delete=False) as tf: + tf.write(src) + tf_path = tf.name + try: + cp = subprocess.run( + ['glslangValidator', '-S', stage, tf_path], + capture_output=True, + ) + self.assertEqual( + cp.returncode, + 0, + f'glslangValidator failed for {stage} shader:\n{cp.stdout.decode()}\n{cp.stderr.decode()}', + ) + finally: + os.unlink(tf_path)