Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions tests/debug_dump_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@
#include "ggml.h"
#include "transcribe-debug.h"

#include <unistd.h> // ::getpid()
#ifdef _WIN32
# include <process.h> // ::_getpid()
#else
# include <unistd.h> // ::getpid()
#endif

#include <cstdio>
#include <cstdlib>
Expand Down Expand Up @@ -70,7 +74,11 @@ int g_failures = 0;
// scoped to this test process. Caller deletes it on success.
fs::path make_unique_dump_dir() {
fs::path base = fs::temp_directory_path() / "transcribe-debug-dump-test";
#ifdef _WIN32
base /= std::to_string(::_getpid());
#else
base /= std::to_string(::getpid());
#endif
fs::remove_all(base); // ensure clean state in case of leftover
fs::create_directories(base);
return base;
Expand Down Expand Up @@ -106,6 +114,16 @@ bool contains(const std::string & haystack, const std::string & needle) {
return haystack.find(needle) != std::string::npos;
}

// Portable setenv(name, value, 1). fs::path::c_str() is wchar_t* on
// Windows, so callers pass a narrow std::string instead.
bool set_env(const char * name, const std::string & value) {
#ifdef _WIN32
return ::_putenv_s(name, value.c_str()) == 0;
#else
return ::setenv(name, value.c_str(), 1) == 0;
#endif
}

} // namespace

int main() {
Expand All @@ -114,8 +132,9 @@ int main() {
// Create a unique dump dir, point TRANSCRIBE_DUMP_DIR at it, and
// initialize the dumper. setenv before init is the contract:
// init() reads the env var once and caches it.
const fs::path dump_dir = make_unique_dump_dir();
if (::setenv("TRANSCRIBE_DUMP_DIR", dump_dir.c_str(), 1) != 0) {
const fs::path dump_dir = make_unique_dump_dir();
const std::string dump_dir_str = dump_dir.string();
if (!set_env("TRANSCRIBE_DUMP_DIR", dump_dir_str)) {
std::fprintf(stderr, "FAIL: setenv failed\n");
return EXIT_FAILURE;
}
Expand All @@ -127,9 +146,10 @@ int main() {
return EXIT_FAILURE;
}
CHECK(transcribe::debug::enabled());
if (transcribe::debug::dump_dir() == nullptr || std::strcmp(transcribe::debug::dump_dir(), dump_dir.c_str()) != 0) {
if (transcribe::debug::dump_dir() == nullptr ||
std::strcmp(transcribe::debug::dump_dir(), dump_dir_str.c_str()) != 0) {
std::fprintf(stderr, "FAIL: dump_dir() = \"%s\", expected \"%s\"\n",
transcribe::debug::dump_dir() ? transcribe::debug::dump_dir() : "(null)", dump_dir.c_str());
transcribe::debug::dump_dir() ? transcribe::debug::dump_dir() : "(null)", dump_dir_str.c_str());
++g_failures;
}

Expand Down
47 changes: 32 additions & 15 deletions tests/whisper_bin_parser_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@
#include "transcribe-bin-loader.h"

#include <sys/stat.h>
#include <unistd.h>
#ifndef _WIN32
# include <unistd.h>
#endif

#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <random>
#include <string>
#include <vector>

Expand Down Expand Up @@ -186,48 +190,61 @@ bool write_synthetic_bin(const std::string & path,
return static_cast<bool>(f);
}

// Portable stand-in for mkstemps(): create a unique empty file named
// transcribe_bin_test_<rand>.bin in the system temp dir. Returns the
// path, or an empty string on failure. Collision-then-overwrite is
// acceptable for these synthetic-fixture tests.
std::string make_temp_bin_path() {
std::error_code ec;
const std::filesystem::path dir = std::filesystem::temp_directory_path(ec);
if (ec) {
return {};
}
std::random_device rd;
const std::filesystem::path p = dir / ("transcribe_bin_test_" + std::to_string(rd()) + ".bin");
std::ofstream f(p, std::ios::binary);
if (!f) {
return {};
}
return p.string();
}

void test_bad_n_fft() {
char tmpl[] = "/tmp/transcribe_bin_test_XXXXXX.bin";
const int fd = ::mkstemps(tmpl, 4);
if (fd < 0) {
const std::string path = make_temp_bin_path();
if (path.empty()) {
std::fprintf(stderr, "SKIP: could not create tempfile for synthetic bin\n");
++g_skipped;
return;
}
::close(fd);
const std::string path = tmpl;

// Whisper-shaped hparams but non-canonical n_fft (200 instead of
// 201). Parser must reject before we even get to the vocab phase.
if (!write_synthetic_bin(path, 51865, 4, 4, 80, 80, 200)) {
std::fprintf(stderr, "SKIP: failed to write synthetic .bin\n");
++g_skipped;
::unlink(path.c_str());
std::remove(path.c_str());
return;
}
transcribe::bin_loader::WhisperBinModel m;
const auto rc = transcribe::bin_loader::parse_whisper_bin(path.c_str(), m);
CHECK(rc == TRANSCRIBE_ERR_GGUF);
::unlink(path.c_str());
std::remove(path.c_str());
}

void test_distil_layer_count_accepted() {
// Distil-style asymmetric layers: n_text_layer=2 with otherwise
// whisper-shaped hparams should pass the hparams gate. We don't
// care that the parser later fails at "no tensors" — the hparams
// check should not be the gating step.
char tmpl[] = "/tmp/transcribe_bin_test_XXXXXX.bin";
const int fd = ::mkstemps(tmpl, 4);
if (fd < 0) {
const std::string path = make_temp_bin_path();
if (path.empty()) {
++g_skipped;
return;
}
::close(fd);
const std::string path = tmpl;

if (!write_synthetic_bin(path, 51865, 24, 2, 80, 80, 201)) {
++g_skipped;
::unlink(path.c_str());
std::remove(path.c_str());
return;
}
transcribe::bin_loader::WhisperBinModel m;
Expand All @@ -237,7 +254,7 @@ void test_distil_layer_count_accepted() {
// "no tensors" / truncated diagnostic), NOT UNSUPPORTED_ARCH —
// that's the proof that the geometry gate accepts distil layers.
CHECK(rc == TRANSCRIBE_ERR_GGUF);
::unlink(path.c_str());
std::remove(path.c_str());
}

} // namespace
Expand Down