Skip to content
Merged
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
37 changes: 37 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Tests

on:
push:
branches: ["*"]
pull_request:
branches: ["*"]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y cmake g++ curl

- name: Install Google Test
run: sudo ./scripts/install_gtest.sh

- name: Discover test files
run: |
TEST_FILES=$(find src -name '*_test.cpp' -type f | sort)
if [ -z "$TEST_FILES" ]; then
echo "ERROR: No *_test.cpp files found"
exit 1
fi
echo "Found test files:"
echo "$TEST_FILES"
COUNT=$(echo "$TEST_FILES" | wc -l)
echo "$COUNT test file(s) discovered"

- name: Build and run tests
run: make test
22 changes: 15 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,43 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")

# Tensor engine library
add_library(tensor_engine STATIC
src/tensor/cpu_engine.cpp
)
target_include_directories(tensor_engine PUBLIC src)

# Linear regression library
add_library(linear_regression STATIC
src/supervised/regression/linear_regression.cpp
)
target_include_directories(linear_regression PUBLIC src)
target_link_libraries(linear_regression PUBLIC tensor_engine)

# --- Executables ---

# Tensor benchmark
add_executable(tensor_benchmark
src/tensor/tensor_benchmark.cpp
)
target_link_libraries(tensor_benchmark tensor_engine)

# Linear regression example
add_executable(linear_regression_example
src/supervised/regression/linear_regression_example.cpp
)
target_link_libraries(linear_regression_example linear_regression)

# Linear regression benchmark
add_executable(linear_regression_benchmark
src/supervised/regression/linear_regression_benchmark.cpp
)
target_link_libraries(linear_regression_benchmark linear_regression)

enable_testing()

include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

add_executable(tensor_test src/tensor/tensor_test.cpp)
target_link_libraries(tensor_test tensor_engine GTest::gtest_main)
include(GoogleTest)
gtest_discover_tests(tensor_test)
125 changes: 90 additions & 35 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ TENSOR_BENCHMARK = $(BUILDDIR)/tensor_benchmark

EXAMPLE_FILES := $(shell find $(SRCDIR) -name "*_example.cpp" 2>/dev/null | grep -v tensor)
BENCHMARK_FILES := $(shell find $(SRCDIR) -name "*_benchmark.cpp" 2>/dev/null | grep -v tensor)
ALGO_TEST_FILES := $(shell find $(SRCDIR) -name "*_test.cpp" 2>/dev/null | grep -v tensor)

ALGORITHMS := $(sort $(patsubst %_example,%,$(patsubst %_benchmark,%,$(basename $(notdir $(EXAMPLE_FILES) $(BENCHMARK_FILES))))))
ALGORITHMS := $(sort $(patsubst %_example,%,$(patsubst %_benchmark,%,$(basename $(notdir $(EXAMPLE_FILES) $(BENCHMARK_FILES) $(ALGO_TEST_FILES))))))

ALGO_SET := $(filter command line environment,$(origin ALGO))

Expand All @@ -25,40 +26,21 @@ $(if $(INVALID_ALGO), $(warning Unknown algorithms: $(INVALID_ALGO)))

ALGO_EXAMPLES := $(addprefix $(BUILDDIR)/,$(addsuffix _example,$(VALID_ALGO)))
ALGO_BENCHMARKS := $(addprefix $(BUILDDIR)/,$(addsuffix _benchmark,$(VALID_ALGO)))
ALGO_TESTS := $(addprefix $(BUILDDIR)/,$(addsuffix _test,$(VALID_ALGO)))

find_algo_src = $(firstword $(shell find $(SRCDIR) -name "$(1).cpp" 2>/dev/null))

$(BUILDDIR)/%_example: $(TENSOR_ENGINE_SRC)
@# Find the example and algorithm source files
$(eval EXAMPLE_SRC := $(call find_algo_src,$(*F)_example))
$(eval ALGO_SRC := $(call find_algo_src,$(*F)))
@if [ -z "$(EXAMPLE_SRC)" ]; then \
echo "Error: Source file for $(*F)_example not found"; \
exit 1; \
fi
@if [ -z "$(ALGO_SRC)" ]; then \
echo "Error: Source file for $(*F) not found"; \
exit 1; \
fi
@mkdir -p $(BUILDDIR)
$(CXX) $(CXXFLAGS) $(INCLUDES) $(EXAMPLE_SRC) $(ALGO_SRC) $< -o $@
# ---- Test infrastructure ----

$(BUILDDIR)/%_benchmark: $(TENSOR_ENGINE_SRC)
@# Find the benchmark and algorithm source files
$(eval BENCHMARK_SRC := $(call find_algo_src,$(*F)_benchmark))
$(eval ALGO_SRC := $(call find_algo_src,$(*F)))
@if [ -z "$(BENCHMARK_SRC)" ]; then \
echo "Error: Source file for $(*F)_benchmark not found"; \
exit 1; \
fi
@if [ -z "$(ALGO_SRC)" ]; then \
echo "Error: Source file for $(*F) not found"; \
exit 1; \
fi
@mkdir -p $(BUILDDIR)
$(CXX) $(CXXFLAGS) $(INCLUDES) $(BENCHMARK_SRC) $(ALGO_SRC) $< -o $@
TEST_FILES := $(shell find $(SRCDIR) -name "*_test.cpp" 2>/dev/null)
TEST_BINS := $(patsubst $(SRCDIR)/%.cpp,$(BUILDDIR)/%,$(TEST_FILES))

GTEST_CXXFLAGS = -std=c++17 -O2 -Wall -Wextra -I$(SRCDIR) -I/usr/local/include
GTEST_LDFLAGS = -L/usr/local/lib -lgtest -lgtest_main -lpthread

.PHONY: all clean list help $(ALGORITHMS)
# ---- Build rules (real targets under build/) ----

.PHONY: all clean list help test $(ALGORITHMS)

ifeq ($(ALGO_SET),)
all: $(TENSOR_BENCHMARK) $(ALGO_EXAMPLES) $(ALGO_BENCHMARKS)
Expand All @@ -76,42 +58,115 @@ $(TENSOR_BENCHMARK): $(SRCDIR)/tensor/tensor_benchmark.cpp $(TENSOR_ENGINE_SRC)
@mkdir -p $(BUILDDIR)
$(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@

$(BUILDDIR)/%_example: $(TENSOR_ENGINE_SRC)
$(eval EXAMPLE_SRC := $(call find_algo_src,$(*F)_example))
$(eval ALGO_SRC := $(call find_algo_src,$(*F)))
@if [ -z "$(EXAMPLE_SRC)" ]; then \
echo "Error: Source file for $(*F)_example not found"; exit 1; fi
@if [ -z "$(ALGO_SRC)" ]; then \
echo "Error: Source file for $(*F) not found"; exit 1; fi
@mkdir -p $(BUILDDIR)
$(CXX) $(CXXFLAGS) $(INCLUDES) $(EXAMPLE_SRC) $(ALGO_SRC) $< -o $@

$(BUILDDIR)/%_benchmark: $(TENSOR_ENGINE_SRC)
$(eval BENCHMARK_SRC := $(call find_algo_src,$(*F)_benchmark))
$(eval ALGO_SRC := $(call find_algo_src,$(*F)))
@if [ -z "$(BENCHMARK_SRC)" ]; then \
echo "Error: Source file for $(*F)_benchmark not found"; exit 1; fi
@if [ -z "$(ALGO_SRC)" ]; then \
echo "Error: Source file for $(*F) not found"; exit 1; fi
@mkdir -p $(BUILDDIR)
$(CXX) $(CXXFLAGS) $(INCLUDES) $(BENCHMARK_SRC) $(ALGO_SRC) $< -o $@

$(BUILDDIR)/%_test: $(SRCDIR)/%_test.cpp $(TENSOR_ENGINE_SRC)
@mkdir -p $(dir $@)
$(CXX) $(GTEST_CXXFLAGS) $< $(TENSOR_ENGINE_SRC) $(GTEST_LDFLAGS) -o $@

$(ALGORITHMS):
@$(MAKE) --no-print-directory ALGO=$@

%_example:
@$(MAKE) --no-print-directory $(BUILDDIR)/$@
$(foreach algo,$(ALGORITHMS),\
$(eval .PHONY: $(algo)_test)\
$(eval $(algo)_test: ; @$$(MAKE) --no-print-directory $$(BUILDDIR)/$(algo)_test)\
)

$(foreach algo,$(ALGORITHMS),\
$(eval .PHONY: $(algo)_example)\
$(eval $(algo)_example: ; @$$(MAKE) --no-print-directory $$(BUILDDIR)/$(algo)_example)\
)

$(foreach algo,$(ALGORITHMS),\
$(eval .PHONY: $(algo)_benchmark)\
$(eval $(algo)_benchmark: ; @$$(MAKE) --no-print-directory $$(BUILDDIR)/$(algo)_benchmark)\
)

test: $(TEST_BINS)
@echo ""
@echo "Running tests..."
@failed=0; total=0; \
for bin in $(TEST_BINS); do \
total=$$((total + 1)); \
echo "--- $$bin ---"; \
if $$bin; then \
echo "PASSED"; \
else \
echo "FAILED"; \
failed=$$((failed + 1)); \
fi; \
echo ""; \
done; \
echo "========================================"; \
echo "Results: $$((total - failed))/$$total passed"; \
if [ $$failed -gt 0 ]; then \
echo "$$failed test suite(s) FAILED"; \
exit 1; \
else \
echo "All test suites passed!"; \
fi

%_benchmark:
@$(MAKE) --no-print-directory $(BUILDDIR)/$@
ifeq ($(ALGO_SET),)
else
override test: TEST_BINS := $(ALGO_TESTS)
endif

list:
@echo "Available algorithms:"
@for algo in $(ALGORITHMS); do echo " - $$algo"; done
@if [ -z "$(ALGORITHMS)" ]; then echo " (none found)"; fi
@echo ""
@echo "Available tests:"
@for f in $(notdir $(TEST_FILES)); do echo " - $$f"; done
@if [ -z "$(TEST_FILES)" ]; then echo " (none found)"; fi
@echo ""
@echo "Usage:"
@echo " make Build all"
@echo " make ALGO=<name> Build specific algorithm"
@echo " make <name> Build specific algorithm (shorthand)"
@echo " make <name>_example Build only example"
@echo " make <name>_benchmark Build only benchmark"
@echo " make <name>_test Build and run specific test"

help:
@echo "ML Library Dynamic Build System"
@echo ""
@echo "Targets:"
@echo " all (default) Build tensor_benchmark + all algorithms"
@echo " test Build and run all *_test.cpp files"
@echo " test ALGO=<name> Run tests for specific algorithm only"
@echo " <algorithm>_test Build and run specific algorithm test"
@echo " tensor_benchmark Build tensor operations benchmark"
@echo " <algorithm> Build specific algorithm (example + benchmark)"
@echo " <algorithm>_example Build only algorithm example"
@echo " <algorithm>_benchmark Build only algorithm benchmark"
@echo " list List available algorithms"
@echo " list List available algorithms and tests"
@echo " help Show this help message"
@echo " clean Remove build directory"
@echo ""
@echo "Examples:"
@echo " make # Build everything"
@echo " make test # Build and run all tests"
@echo " make test ALGO=linear_regression # Run linear_regression tests only"
@echo " make linear_regression_test # Build and run linear_regression test"
@echo " make linear_regression # Build linear regression only"
@echo " make ALGO=linear_regression # Same as above"
@echo " make linear_regression_example # Build only example"
Expand Down
26 changes: 26 additions & 0 deletions scripts/install_gtest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail

if [ -f /usr/local/include/gtest/gtest.h ] && \
[ -f /usr/local/lib/libgtest.a ] && \
[ -f /usr/local/lib/libgtest_main.a ]; then
echo "Google Test already installed — skipping."
exit 0
fi

echo "Installing Google Test v1.14.0..."
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

curl -sL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz \
| tar xz -C "$TMPDIR"

cmake -S "$TMPDIR/googletest-1.14.0" -B "$TMPDIR/build" \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF

cmake --build "$TMPDIR/build" -j"$(nproc)"
cmake --install "$TMPDIR/build"

echo "Google Test installed successfully."
Loading
Loading