Skip to content

Commit

Permalink
cest-runner finds all tests and runs them
Browse files Browse the repository at this point in the history
  • Loading branch information
cegonse committed Nov 15, 2024
1 parent d8c08c1 commit b16206f
Show file tree
Hide file tree
Showing 15 changed files with 298 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ build/
# Test results
log.xml
test_summary.jsonl
runner/test/test_runner
test/examples/test_assertions
test/examples/test_chatbot
test/examples/test_description
Expand Down
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@ ifeq ($(UNAME_S),Darwin)
endif


RUNNER_SRCS := runner/main.cpp runner/directory.cpp runner/process.cpp runner/output.cpp runner/runner.cpp
TEST_SRCS := $(shell find test -name '*.cpp')
TESTS := $(basename $(TEST_SRCS))


test: all

all: build $(TESTS) run

runner: build
g++ $(RUNNER_SRCS) -std=c++17 -g -O0 -o build/cest-runner
g++ runner/test/runner.test.cpp runner/runner.cpp runner/test/helpers/helpers.cpp -Ibuild -std=c++17 -g -O0 -o runner/test/test_runner

runner-tests: runner
cd runner && ../build/cest-runner

build:
mkdir -p build
quom src/main.hpp build/cest
Expand All @@ -41,5 +48,6 @@ clean:
@rm -f log.xml
@rm -f *.jsonl
@rm -rf build
@rm -rf runner/test/test_runner

.PHONY: clean run build test
.PHONY: clean run build test runner runner-tests
62 changes: 62 additions & 0 deletions runner/directory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include "directory.h"
#include <filesystem>
#include <algorithm>

constexpr bool hasPerms(std::filesystem::perms target, std::filesystem::perms other)
{
return std::filesystem::perms::none != (other & target);
}

constexpr bool isExecutable(std::filesystem::perms target)
{
return hasPerms(std::filesystem::perms::owner_exec, target) ||
hasPerms(std::filesystem::perms::group_exec, target) ||
hasPerms(std::filesystem::perms::others_exec, target);
}

static std::vector<std::string> filterBy(const std::string& filter, const std::vector<std::string>& entries)
{
std::vector<std::string> result;

std::copy_if(
entries.begin(),
entries.end(),
std::back_inserter(result),
[&filter](std::string path) {
return path.find(filter) != std::string::npos;
}
);

return result;
}

std::vector<std::string> Directory::findExecutableFiles(
const std::string& path,
const std::string& filter
) {
std::vector<std::string> result;

if (!std::filesystem::is_directory(path)) return result;

for (const auto& entry : std::filesystem::directory_iterator(path))
{
const auto permissions = entry.status().permissions();

if (entry.is_regular_file() && isExecutable(permissions))
{
result.push_back(entry.path());
}
else if (entry.is_directory())
{
const auto sub_files = Directory::findExecutableFiles(entry.path(), filter);
result.insert(result.end(), sub_files.begin(), sub_files.end());
}
}

return filterBy(filter, result);
}

std::string Directory::cwd()
{
return std::filesystem::current_path();
}
9 changes: 9 additions & 0 deletions runner/directory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once
#include <vector>
#include <string>

namespace Directory
{
std::vector<std::string> findExecutableFiles(const std::string& path, const std::string& filter);
std::string cwd();
}
7 changes: 7 additions & 0 deletions runner/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "runner.h"

int main(int argc, char *argv[])
{
Runner::runTestsInCurrentPath();
return 0;
}
8 changes: 8 additions & 0 deletions runner/output.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "output.h"
#include <iostream>

void Output::print(std::stringstream& text)
{
std::cout << text.str();
text.clear();
}
7 changes: 7 additions & 0 deletions runner/output.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include <sstream>

namespace Output
{
void print(std::stringstream& text);
}
69 changes: 69 additions & 0 deletions runner/process.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "process.h"
#include <unistd.h>
#include <sys/wait.h>

static constexpr int MAX_ARGS = 32;
static constexpr int MAX_BUFFER = 4096;

static constexpr bool isChildProcess(pid_t pid)
{
return pid == 0;
}

static void handleChildProcess(int pipe_fd[2], const std::string& path, std::vector<std::string> args)
{
std::array<char *, MAX_ARGS> c_args;

dup2(pipe_fd[1], STDOUT_FILENO);
close(STDERR_FILENO);
close(pipe_fd[0]);
close(pipe_fd[1]);

c_args.fill(NULL);

for (int i=0; i<args.size(); ++i)
{
c_args[i] = (char *)args[i].c_str();
}

execv(path.c_str(), c_args.data());
}

static void waitForChildren()
{
pid_t pid;
int status = 0;

while ((pid = wait(&status)) > 0);
}

static void handleParentProcess(int pipe_fd[2], std::function<void(std::string)> on_output)
{
std::array<char, MAX_BUFFER> buffer;

buffer.fill('\0');
close(pipe_fd[1]);
read(pipe_fd[0], buffer.data(), buffer.size());

on_output(std::string(buffer.data()));

waitForChildren();
}

void Process::runExecutable(const std::string& path, std::function<void(std::string)> on_output)
{
int pipe_fd[2];
pipe(pipe_fd);

const auto pid = fork();

if (isChildProcess(pid))
{
std::vector<std::string> args = { "-o" };
handleChildProcess(pipe_fd, path, args);
}
else
{
handleParentProcess(pipe_fd, on_output);
}
}
7 changes: 7 additions & 0 deletions runner/process.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <functional>
#include <string>

namespace Process
{
void runExecutable(const std::string& path, std::function<void(std::string)> on_output);
}
21 changes: 21 additions & 0 deletions runner/runner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "runner.h"
#include "process.h"
#include "directory.h"
#include "output.h"

void Runner::runTestsInCurrentPath()
{
std::stringstream out;
const auto executables = Directory::findExecutableFiles(Directory::cwd(), "test_");

for (const auto& test_file : executables)
{
out << "Running test " << test_file << std::endl;
Output::print(out);

Process::runExecutable(test_file, [&out](const auto& output) {
out << output;
Output::print(out);
});
}
}
6 changes: 6 additions & 0 deletions runner/runner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

namespace Runner
{
void runTestsInCurrentPath();
}
56 changes: 56 additions & 0 deletions runner/test/helpers/helpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include <sstream>
#include <vector>
#include <functional>
#include "helpers.h"
#include "../../output.h"
#include "../../process.h"
#include "../../directory.h"

static std::vector<std::string> __mock_executable_files;
static bool __find_executable_files_called = false;
static std::string __find_executable_files_path;
static std::string __find_executable_files_filter;

static std::string __mock_run_executable_output;
static bool __run_executable_has_been_called = false;
static std::vector<std::string> __run_executable_path;

void Output::print(std::stringstream& text)
{
}

void Directory::findExecutableFiles_mockFiles(std::vector<std::string> files)
{
__mock_executable_files = files;
}
bool Directory::findExecutableFiles_hasBeenCalledWith(const std::string& path, const std::string& filter)
{
return __find_executable_files_called && __find_executable_files_path == path && __find_executable_files_filter == filter;
}
std::vector<std::string> Directory::findExecutableFiles(const std::string& path, const std::string& filter)
{
__find_executable_files_path = path;
__find_executable_files_filter = filter;
__find_executable_files_called = true;
return __mock_executable_files;
}
std::string Directory::cwd()
{
return "/cwd";
}

void Process::runExecutable_mockOutput(const std::string& output)
{
__mock_run_executable_output = output;
}
void Process::runExecutable(const std::string& path, std::function<void(std::string)> on_output)
{
__run_executable_has_been_called = true;
__run_executable_path.push_back(path);
on_output(__mock_run_executable_output);
}
bool Process::runExecutable_hasBeenCalledWith(const std::string& path)
{
auto it = std::find(__run_executable_path.cbegin(), __run_executable_path.cend(), path);
return __run_executable_has_been_called && it != __run_executable_path.end();
}
16 changes: 16 additions & 0 deletions runner/test/helpers/helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once
#include <sstream>
#include <functional>
#include <vector>

namespace Directory
{
void findExecutableFiles_mockFiles(std::vector<std::string> files);
bool findExecutableFiles_hasBeenCalledWith(const std::string& path, const std::string& filter);
}

namespace Process
{
void runExecutable_mockOutput(const std::string& output);
bool runExecutable_hasBeenCalledWith(const std::string& path);
}
17 changes: 17 additions & 0 deletions runner/test/runner.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <cest>
#include "../runner.h"
#include "../directory.h"
#include "helpers/helpers.h"

describe("Runner", []() {
it("executes all tests found in the current directory", []() {
std::vector<std::string> test_files = { "first/test", "second/test" };
Directory::findExecutableFiles_mockFiles(test_files);

Runner::runTestsInCurrentPath();

expect(Directory::findExecutableFiles_hasBeenCalledWith(Directory::cwd(), "test_")).toBeTruthy();
expect(Process::runExecutable_hasBeenCalledWith("first/test")).toBeTruthy();
expect(Process::runExecutable_hasBeenCalledWith("second/test")).toBeTruthy();
});
});
4 changes: 2 additions & 2 deletions src/arg-parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ namespace cest
}
catch (const std::invalid_argument &err)
{
std::cout << "Invalid seed value: " << argv[i + 1] << std::endl;
std::cerr << "Invalid seed value: " << argv[i + 1] << std::endl;
}
}
}
Expand All @@ -59,4 +59,4 @@ namespace cest

return options;
}
}
}

0 comments on commit b16206f

Please sign in to comment.