Skip to content

Commit cb89fe1

Browse files
bghgaryCopilot
andauthored
Write shaderCache.bin next to the executable instead of CWD (#1670)
## Problem `ShaderCache.SaveAndLoad` writes `shaderCache.bin` to the current working directory: ```cpp static const char* shaderCacheFileName = "shaderCache.bin"; std::ofstream stream(shaderCacheFileName, std::ios::binary); ``` When `UnitTests.exe` is launched from anywhere that isn't the build output directory (e.g. the repo root), the file lands in that directory and is never cleaned up. A unit test should not leave artifacts in the user's working directory. ## Fix Write the file next to the executable instead. Added a `GetExecutableDirectory()` helper declared in `App.h` and implemented per-platform: - **Win32** — `GetModuleFileNameW(nullptr, ...)` - **Apple** — `_NSGetExecutablePath` + `std::filesystem::canonical` - **X11/Linux** — `std::filesystem::canonical("/proc/self/exe")` The test now uses `GetExecutableDirectory() / "shaderCache.bin"`, asserts each stream opened successfully, and asserts on the result of `std::filesystem::remove` so silent cleanup failures are visible. No collisions between concurrent runs are possible (each CMake build has its own `UnitTests.exe`), and artifacts land alongside the build output rather than in the user's current directory or system temp. ## Testing Ran `UnitTests.exe --gtest_filter="ShaderCache.*"` from the repo root: test passes, no `shaderCache.bin` left behind at the repo root or next to the executable. [Created by Copilot on behalf of @bghgary] --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fb416e3 commit cb89fe1

5 files changed

Lines changed: 48 additions & 4 deletions

File tree

Apps/UnitTests/Source/App.Apple.mm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
#include "App.h"
22
#include <Babylon/DebugTrace.h>
33

4+
#include <mach-o/dyld.h>
5+
6+
std::filesystem::path GetExecutableDirectory()
7+
{
8+
uint32_t size = 0;
9+
_NSGetExecutablePath(nullptr, &size);
10+
std::string buffer(size, '\0');
11+
if (_NSGetExecutablePath(buffer.data(), &size) != 0)
12+
{
13+
throw std::runtime_error{"_NSGetExecutablePath failed"};
14+
}
15+
return std::filesystem::canonical(buffer).parent_path();
16+
}
17+
418
int main(int argc, char* argv[])
519
{
620
Babylon::DebugTrace::EnableDebugTrace(true);

Apps/UnitTests/Source/App.Win32.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
77
return ::DefWindowProc(hWnd, msg, wParam, lParam);
88
}
99

10+
std::filesystem::path GetExecutableDirectory()
11+
{
12+
wchar_t buffer[MAX_PATH];
13+
const DWORD length = GetModuleFileNameW(nullptr, buffer, MAX_PATH);
14+
if (length == 0 || length == MAX_PATH)
15+
{
16+
throw std::runtime_error{"GetModuleFileNameW failed"};
17+
}
18+
return std::filesystem::path{buffer}.parent_path();
19+
}
20+
1021
int main(int argc, char* argv[])
1122
{
1223
SetConsoleOutputCP(CP_UTF8);

Apps/UnitTests/Source/App.X11.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ namespace
1616
constexpr const char* wmDeleteWindowName = "WM_DELETE_WINDOW";
1717
}
1818

19+
std::filesystem::path GetExecutableDirectory()
20+
{
21+
return std::filesystem::canonical("/proc/self/exe").parent_path();
22+
}
23+
1924
int main(int argc, char* argv[])
2025
{
2126
XInitThreads();

Apps/UnitTests/Source/App.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@
22

33
#include <Babylon/Graphics/Device.h>
44

5-
int RunTests(const Babylon::Graphics::Configuration& config, int argc, char* argv[]);
5+
#include <filesystem>
6+
7+
int RunTests(const Babylon::Graphics::Configuration& config, int argc, char* argv[]);
8+
9+
std::filesystem::path GetExecutableDirectory();

Apps/UnitTests/Source/Tests.ShaderCache.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
#include <Babylon/Plugins/ShaderCache.h>
99
#include <Babylon/ScriptLoader.h>
1010

11+
#include "App.h"
12+
1113
#include <chrono>
1214
#include <cstdlib>
15+
#include <filesystem>
1316
#include <optional>
1417
#include <future>
1518
#include <iostream>
@@ -76,18 +79,25 @@ TEST(ShaderCache, SaveAndLoad)
7679
update.Start();
7780
}
7881

79-
static const char* shaderCacheFileName = "shaderCache.bin";
82+
const auto shaderCachePath = GetExecutableDirectory() / "shaderCache.bin";
83+
8084
uint32_t shaderCount{};
8185
{
82-
std::ofstream stream(shaderCacheFileName, std::ios::binary);
86+
std::ofstream stream(shaderCachePath, std::ios::binary);
87+
ASSERT_TRUE(stream.is_open()) << "Failed to open for write: " << shaderCachePath;
8388
shaderCount = Babylon::Plugins::ShaderCache::Save(stream);
8489
EXPECT_EQ(shaderCount, 1);
8590
}
8691
{
87-
std::ifstream stream(shaderCacheFileName, std::ios::binary);
92+
std::ifstream stream(shaderCachePath, std::ios::binary);
93+
ASSERT_TRUE(stream.is_open()) << "Failed to open for read: " << shaderCachePath;
8894
auto deserializedCount = Babylon::Plugins::ShaderCache::Load(stream);
8995
EXPECT_EQ(deserializedCount, shaderCount);
9096
}
97+
std::error_code ec;
98+
const auto removed = std::filesystem::remove(shaderCachePath, ec);
99+
EXPECT_FALSE(ec) << "Failed to remove " << shaderCachePath << ": " << ec.message();
100+
EXPECT_TRUE(removed) << "Expected shader cache file to be removed: " << shaderCachePath;
91101

92102
update.Finish();
93103
device.FinishRenderingCurrentFrame();

0 commit comments

Comments
 (0)