# Copyright Cartesi and individual authors (see AUTHORS)
# SPDX-License-Identifier: LGPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program (see COPYING). If not, see <https://www.gnu.org/licenses/>.
#

PREFIX = /opt/cartesi
INSTALLDIR = $(PREFIX)/tests

DEPENDENCIES_LOCK ?= $(abspath ../dependencies.lock)
include $(DEPENDENCIES_LOCK)

LUA_BIN ?= $(shell which lua5.4)

BUILDDIR = $(abspath build)
RISCV_PREFIX = riscv64-unknown-elf-
RISCV_GCC_OPTS = -static -mcmodel=medany -fvisibility=hidden -ffreestanding -nostdlib -nostartfiles -I/usr/riscv64-linux-gnu/include

MACHINE_EMULATOR_SRC_DIR = $(abspath ../src)

MACHINE_EMULATOR_SO_VERSION= $(shell make -sC ../src so-version)

TARGETS_WITH_TOOLCHAIN = $(addsuffix -with-toolchain,build-tests-machine build-tests-uarch machine uarch misc riscv-tests riscv-arch-test)

CARTESI_IMAGES_PATH ?= $(abspath ./build/images)
CARTESI_CMIO_PATH ?= $(abspath ./build/cmio)
CARTESI_STEP_LOGS_PATH ?= $(abspath ./build/step-logs)
CARTESI_TESTS_PATH ?= $(abspath ./build/machine)
CARTESI_TESTS_UARCH_PATH ?= $(abspath ./build/uarch)
CARTESI_COMPUTATION_HASH_CORPUS_PATH ?= $(abspath ./build/computation-hash-corpus)

export CARTESI_IMAGES_PATH
export CARTESI_CMIO_PATH
export CARTESI_STEP_LOGS_PATH
export CARTESI_TESTS_PATH
export CARTESI_TESTS_UARCH_PATH
export CARTESI_COMPUTATION_HASH_CORPUS_PATH

COMPUTATION_HASH_GUEST = $(CARTESI_TESTS_PATH)/computation_hash.bin
COMPUTATION_HASH_UARCH_NEAR_LIMIT = $(CARTESI_TESTS_UARCH_PATH)/rv64ui-uarch-computation-hash-near-limit.bin
COMPUTATION_HASH_UARCH_OVERFLOW = $(CARTESI_TESTS_UARCH_PATH)/rv64ui-uarch-computation-hash-overflow.bin
COMPUTATION_HASH_MANIFEST = $(CARTESI_COMPUTATION_HASH_CORPUS_PATH)/manifest.json
COMPUTATION_HASH_GENERATOR = lua/create-computation-hash-corpus.lua
COMPUTATION_HASH_ARCHIVE = computation-hash-corpus.tar.gz

TARGET_OS ?= $(shell uname -s)
ifeq ($(TARGET_OS),Darwin)
LIBCARTESI_SO=libcartesi-$(MACHINE_EMULATOR_SO_VERSION).dylib
LIBCARTESI_SO_JSONRPC=libcartesi_jsonrpc-$(MACHINE_EMULATOR_SO_VERSION).dylib
else
LIBCARTESI_SO=libcartesi-$(MACHINE_EMULATOR_SO_VERSION).so
LIBCARTESI_SO_JSONRPC=libcartesi_jsonrpc-$(MACHINE_EMULATOR_SO_VERSION).so
endif

NUM_JOBS ?= $(shell getconf _NPROCESSORS_ONLN)

LUAROCKS_PATH := $(shell luarocks --lua-version 5.4 path --lr-path 2>/dev/null)
LUAROCKS_CPATH := $(shell luarocks --lua-version 5.4 path --lr-cpath 2>/dev/null)
export LUA_PATH_5_4 := $(LUA_PATH_5_4);$(LUAROCKS_PATH)
export LUA_CPATH_5_4 := $(LUA_CPATH_5_4);$(LUAROCKS_CPATH)

export CM_CLI ?= $(abspath ../src/cartesi-machine.lua)
LUA_CLI ?= $(LUA)

# Filter forwarded to a spec run. Empty matches all tests. Example:
#   make run-lua-spec-collect-hashes SPEC_FILTER='dense uarch'
SPEC_FILTER ?=

LUACOV_DIR := $(BUILDDIR)/luacov

ifeq ($(coverage),yes)
# The lua test runners fork workers whose duplicate counter dumps would race the
# shared .gcda files. Parallelism comes from make -j across test targets instead.
override NUM_JOBS = 1
export LUA_CLI = $(LUA) -lluacov
LUACOV_STATS := $(LUACOV_DIR)/luacov.stats.out
LUACOV_CONFIG := $(abspath ./scripts/luacov.lua)
export LUACOV_STATS
export LUACOV_CONFIG
$(LUACOV_DIR):
	mkdir -p $@
# spec-cm-cli's CLI children and test-evmu write luacov.stats.out under this dir,
# so it must exist before them. They are also ordered so their writes do not race.
LUACOV_DIR_ORDER := $(LUACOV_DIR)
TEST_EVMU_ORDER := $(LUACOV_DIR) run-lua-spec-cm-cli
TEST_COMPUTATION_HASH_ORDER := $(LUACOV_DIR) run-lua-spec-cm-cli test-evmu
endif

ifeq ($(TARGET_OS),Darwin)
COVERAGE_TOOLCHAIN?=clang
else
COVERAGE_TOOLCHAIN?=gcc
endif
export COVERAGE_TOOLCHAIN
COVERAGE_OUTPUT_DIR=$(BUILDDIR)/coverage
COVERAGE_WORKLOAD=stress-ng --cpu 1 --cpu-ops 1
COVERAGE_SOURCES=$(wildcard ../src/*.h) $(wildcard ../src/*.hpp) $(wildcard ../src/*.cpp)

ifeq ($(sanitize),yes)
ifeq ($(TARGET_OS),Darwin)
LIBASAN_SO=$(shell realpath `$(CC) -print-file-name=libclang_rt.asan_osx_dynamic.dylib`)
LD_PRELOAD_PREFIX=DYLD_INSERT_LIBRARIES="$(LIBASAN_SO)"
else
LIBASAN_SO=$(shell realpath `$(CC) -print-file-name=libasan.so`)
LIBSTDCPP_SO=$(shell realpath `$(CC) -print-file-name=libstdc++.so`)
LD_PRELOAD="$(LIBASAN_SO) $(LIBSTDCPP_SO)"
LD_PRELOAD_PREFIX=LD_PRELOAD=$(LD_PRELOAD)
endif
LUA=$(LD_PRELOAD_PREFIX) $(LUA_BIN)
else
LUA=$(LUA_BIN)
endif

RUN_ARCH_TEST = $(LUA) $(abspath ./lua/run-rv64i-arch-test.lua)

MACHINE_GUEST_TOOLS_ROOTFS_URL = https://github.com/cartesi/machine-guest-tools/releases/download/$(MACHINE_GUEST_TOOLS_TAG)/rootfs-tools.ext2
MACHINE_LINUX_IMAGE_URL = https://github.com/cartesi/machine-linux-image/releases/download/$(MACHINE_LINUX_IMAGE_TAG)/$(MACHINE_LINUX_IMAGE_FILENAME)
ROOTFS_TOOLS_IMAGE = $(CARTESI_IMAGES_PATH)/rootfs-tools.ext2
LINUX_IMAGE = $(CARTESI_IMAGES_PATH)/$(MACHINE_LINUX_IMAGE_FILENAME)
ROOTFS_TOOLS_INPUTS_HASH := $(shell printf '%s\n' \
	'$(MACHINE_GUEST_TOOLS_TAG)' \
	'$(MACHINE_GUEST_TOOLS_ROOTFS_SHA256)' | shasum -a 256 | head -c 64)
LINUX_IMAGE_INPUTS_HASH := $(shell printf '%s\n' \
	'$(MACHINE_LINUX_IMAGE_TAG)' \
	'$(MACHINE_LINUX_IMAGE_FILENAME)' \
	'$(MACHINE_LINUX_IMAGE_SHA256)' | shasum -a 256 | head -c 64)
ROOTFS_TOOLS_INPUTS_KEY = $(CARTESI_IMAGES_PATH)/.rootfs-tools-$(ROOTFS_TOOLS_INPUTS_HASH)
LINUX_IMAGE_INPUTS_KEY = $(CARTESI_IMAGES_PATH)/.linux-$(LINUX_IMAGE_INPUTS_HASH)
CARTESI_IMAGES = $(CARTESI_IMAGES_PATH)/linux.bin $(CARTESI_IMAGES_PATH)/rootfs.ext2

STYLUA=stylua
STYLUA_FLAGS=--indent-type Spaces --respect-ignores

.DELETE_ON_ERROR:

build-tests-all: misc build-tests-machine-with-toolchain build-tests-uarch-with-toolchain images $(CARTESI_CMIO_PATH) $(CARTESI_STEP_LOGS_PATH)

build-tests-machine: machine riscv-tests

build-tests-fuzz-seed-corpus:
ifneq (yes,$(filter yes,$(sanitize) $(coverage)))
	@$(MAKE) -C fuzz fuzz-seed-corpus
endif

build-tests-uarch: uarch riscv-arch-test

build-tests-misc: misc build-tests-fuzz-seed-corpus

build-tests-fuzz:
	@$(MAKE) -C fuzz CC=clang CXX=clang++ fuzz=yes all

build-tests-images: images

build-tests-computation-hash-fixtures: $(COMPUTATION_HASH_GUEST) \
	$(COMPUTATION_HASH_UARCH_NEAR_LIMIT) $(COMPUTATION_HASH_UARCH_OVERFLOW)

$(COMPUTATION_HASH_GUEST): machine/src/computation_hash.S
	@$(MAKE) -C machine/src BUILDDIR=$(CARTESI_TESTS_PATH) $@

$(COMPUTATION_HASH_UARCH_NEAR_LIMIT) $(COMPUTATION_HASH_UARCH_OVERFLOW): \
	$(CARTESI_TESTS_UARCH_PATH)/rv64ui-uarch-%.bin: uarch/%.S
	@$(MAKE) -C uarch BUILDDIR=$(CARTESI_TESTS_UARCH_PATH) $@

build-computation-hash-corpus-with-toolchain:
	$(MAKE) toolchain-exec CONTAINER_COMMAND="make build-tests-computation-hash-fixtures"
	$(MAKE) create-computation-hash-corpus

build-computation-hash-corpus: build-tests-computation-hash-fixtures
	$(MAKE) create-computation-hash-corpus

create-computation-hash-corpus: clean-computation-hash-corpus
	$(LUA) ./lua/create-computation-hash-corpus.lua
	RECORD_COMPUTATION_HASH_CORPUS=yes $(LUA) ./lua/test-computation-hash.lua

archive-computation-hash-corpus: $(COMPUTATION_HASH_MANIFEST)
	tar -czf $(COMPUTATION_HASH_ARCHIVE) -C $(BUILDDIR) computation-hash-corpus

# Regenerate an existing artifact when its generator changes. Building missing fixture binaries
# remains an explicit toolchain-managed step.
$(COMPUTATION_HASH_MANIFEST): $(COMPUTATION_HASH_GENERATOR)
	@test -f $(COMPUTATION_HASH_GUEST) -a -f $(COMPUTATION_HASH_UARCH_NEAR_LIMIT) \
		-a -f $(COMPUTATION_HASH_UARCH_OVERFLOW) || \
		{ echo 'Run make build-computation-hash-corpus-with-toolchain first' >&2; exit 1; }
	@rm -rf $(CARTESI_COMPUTATION_HASH_CORPUS_PATH)
	$(MAKE) create-computation-hash-corpus

build-tests-uarch-with-toolchain uarch-with-toolchain riscv-arch-test-with-toolchain:
machine uarch misc:
	@$(MAKE) $(BUILDDIR)/$@
	@$(MAKE) -C $@ BUILDDIR=$(BUILDDIR)/$@

riscv-tests:  $(BUILDDIR)/riscv-tests/Makefile | $(BUILDDIR)/machine
	@cd $(BUILDDIR)/$@ && ../../../third-party/riscv-tests/configure
	@$(MAKE) -C $(BUILDDIR)/$@ RISCV_PREFIX=$(RISCV_PREFIX) RISCV_GCC_OPTS="$(RISCV_GCC_OPTS)" isa
	@cp -a $(BUILDDIR)/riscv-tests/isa/*.bin $(BUILDDIR)/riscv-tests/isa/*.dump $(BUILDDIR)/machine
	@cd $(BUILDDIR)/riscv-tests/isa && find . -maxdepth 1 -type f ! -name "*.*" -exec cp -a {} $(BUILDDIR)/machine/{}.elf \;

$(BUILDDIR)/riscv-tests/Makefile: | $(BUILDDIR)/riscv-tests
	@cd $(BUILDDIR)/riscv-tests && ../../../third-party/riscv-tests/configure

riscv-arch-test:
	@$(MAKE) $(BUILDDIR)/uarch-$@
	@$(MAKE) -C ../third-party/riscv-arch-test/riscv-target/cartesi TOOLCHAIN_PREFIX=$(RISCV_PREFIX) MACHINE_EMULATOR_SRC_DIR=$(MACHINE_EMULATOR_SRC_DIR)
	@$(MAKE) -C ../third-party/riscv-arch-test build XLEN=64 RISCV_TARGET=cartesi RISCV_ISA=rv64i RISCV_DEVICE=I TOOLCHAIN_PREFIX=$(RISCV_PREFIX) MACHINE_EMULATOR_SRC_DIR=$(MACHINE_EMULATOR_SRC_DIR) WORK=$(BUILDDIR)/uarch-$@ JOBS=-j$$(nproc)

$(BUILDDIR)/%:
	@mkdir -p $@

$(ROOTFS_TOOLS_INPUTS_KEY) $(LINUX_IMAGE_INPUTS_KEY): | $(CARTESI_IMAGES_PATH)
	touch $@

$(ROOTFS_TOOLS_IMAGE): $(ROOTFS_TOOLS_INPUTS_KEY)
	wget -q -O $@ $(MACHINE_GUEST_TOOLS_ROOTFS_URL)
	printf '%s  %s\n' '$(MACHINE_GUEST_TOOLS_ROOTFS_SHA256)' '$@' | shasum -a 256 -c -
	touch $@

$(LINUX_IMAGE): $(LINUX_IMAGE_INPUTS_KEY)
	wget -q -O $@ $(MACHINE_LINUX_IMAGE_URL)
	printf '%s  %s\n' '$(MACHINE_LINUX_IMAGE_SHA256)' '$@' | shasum -a 256 -c -
	touch $@

$(CARTESI_IMAGES_PATH)/rootfs.ext2: $(ROOTFS_TOOLS_IMAGE)
	ln -sf $(notdir $<) $@

$(CARTESI_IMAGES_PATH)/linux.bin: $(LINUX_IMAGE)
	ln -sf $(notdir $<) $@

images: $(CARTESI_IMAGES)

clean:
	@D=../third-party/riscv-arch-test/riscv-target/cartesi; \
		if [ -d $$D ]; then $(MAKE) -C $$D clean; fi
	@$(MAKE) -C misc clean
	@$(MAKE) -C fuzz clean
	@rm -f *.profdata *.profraw *.gcda *.gcov
	@rm -rf $(BUILDDIR)

toolchain-env:
	@docker run --hostname toolchain-env -it --rm \
		-e USER=$$(id -u -n) \
		-e GROUP=$$(id -g -n) \
		-e UID=$$(id -u) \
		-e GID=$$(id -g) \
		-v `pwd`/../:/usr/src/emulator \
		-w /usr/src/emulator/tests \
		cartesi/machine-emulator:toolchain /bin/bash

toolchain-exec:
	@docker run --hostname toolchain-env --rm \
		-e USER=$$(id -u -n) \
		-e GROUP=$$(id -g -n) \
		-e UID=$$(id -u) \
		-e GID=$$(id -g) \
		-v `pwd`/../:/usr/src/emulator \
		-w /usr/src/emulator/tests \
		cartesi/machine-emulator:toolchain $(CONTAINER_COMMAND)

emulator-builder-exec:
	@docker run --hostname toolchain-env --rm \
		-e USER=$$(id -u -n) \
		-e GROUP=$$(id -g -n) \
		-e UID=$$(id -u) \
		-e GID=$$(id -g) \
		-v `pwd`:/usr/src/emulator/tests \
		-w /usr/src/emulator/tests \
		cartesi/machine-emulator:builder $(CONTAINER_COMMAND)

$(TARGETS_WITH_TOOLCHAIN):
	$(MAKE) toolchain-exec CONTAINER_COMMAND="make -j\$$(nproc) $(subst -with-toolchain,,$@)"

build-tests-misc-with-builder-image:
	$(MAKE) emulator-builder-exec CONTAINER_COMMAND="make -j\$$(nproc) build-tests-misc"

create-machines $(CARTESI_CMIO_PATH): | $(CARTESI_IMAGES)
	@rm -rf $(CARTESI_CMIO_PATH)
	$(LUA) ./lua/create-machines.lua

clean-machines:
	@rm -rf $(CARTESI_CMIO_PATH)

clean-computation-hash-corpus:
	@rm -rf $(CARTESI_COMPUTATION_HASH_CORPUS_PATH) $(COMPUTATION_HASH_ARCHIVE)

test-computation-hash: verify-existing-computation-hash-corpus

# Downloaded corpora are immutable test inputs: deliberately do not make this target depend on
# the generator or fixture timestamps.
verify-existing-computation-hash-corpus: | $(TEST_COMPUTATION_HASH_ORDER)
	$(LUA_CLI) ./lua/test-computation-hash.lua


create-step-logs $(CARTESI_STEP_LOGS_PATH): | $(CARTESI_IMAGES)
	$(LUA) ./lua/create-step-logs.lua
	$(LUA) ./lua/cartesi-machine-tests.lua --jobs=$(NUM_JOBS) --hash-function=sha256 --save-step-logs=$(CARTESI_STEP_LOGS_PATH) run_step

clean-step-logs:
	@rm -rf $(CARTESI_STEP_LOGS_PATH)

test-cmio: | $(CARTESI_CMIO_PATH)
	@./scripts/test-cmio.sh ../src/cartesi-jsonrpc-machine '$(LUA) ../src/cartesi-machine.lua'

test-machine:
	$(LUA) ./lua/cartesi-machine-tests.lua --jobs=$(NUM_JOBS) run

test-machine-with-log-step:
	$(LUA) ./lua/cartesi-machine-tests.lua --jobs=$(NUM_JOBS) --hash-function=keccak256 run_step
	$(LUA) ./lua/cartesi-machine-tests.lua --jobs=$(NUM_JOBS) --hash-function=sha256 run_step

test-uarch:
	$(LUA) ./lua/cartesi-machine-tests.lua --jobs=$(NUM_JOBS) run_uarch

test-uarch-compare:
	$(LUA) ./lua/cartesi-machine-tests.lua --test="^rv64ui.*$$" --concurrency=update_hash_tree:1 --jobs=$(NUM_JOBS) run_host_and_uarch

test-uarch-rv64ui:
	$(LUA) ./lua/uarch-riscv-tests.lua --jobs=$(NUM_JOBS) run

test-uarch-interpreter:
	$(MAKE) -C ../third-party/riscv-arch-test verify XLEN=64 RISCV_TARGET=cartesi RISCV_ISA=rv64i RISCV_DEVICE=I WORK=$(BUILDDIR)/uarch-riscv-arch-test RUN_ARCH_TEST='$(RUN_ARCH_TEST)'

test-jsonrpc:
	./scripts/test-jsonrpc-server.sh ../src/cartesi-jsonrpc-machine '$(LUA) ../src/cartesi-machine.lua' '$(LUA) ./lua/cartesi-machine-tests.lua' '$(LUA)'

# Plain lua test programs, each run as its own process with the "local" machine type.
LUA_PROGRAMS = htif-console htif-cmio htif-yield log-with-mtime-transition \
	machine-bind machine-test mcycle-overflow mtime-interrupt

# Lester spec suites, each run as its own process so they parallelize. cm-cli and
# evmu are excluded here because they need the luacov-aware rules below.
LUA_SPECS := $(filter-out cm-cli evmu,$(patsubst lua/spec-%.lua,%,$(wildcard lua/spec-*.lua)))

run-lua-program-%: | $(CARTESI_IMAGES)
	$(LUA) lua/$*.lua local

run-lua-spec-%: | $(CARTESI_IMAGES)
	$(LUA) lua/spec-$*.lua --filter='$(SPEC_FILTER)'

# spec-cm-cli runs the CLI as child processes under LUA_CLI (lua -lluacov under
# coverage), which write luacov.stats.out, so its directory must exist first.
run-lua-spec-cm-cli: | $(CARTESI_IMAGES) $(LUACOV_DIR_ORDER)
	$(LUA) lua/spec-cm-cli.lua --filter='$(SPEC_FILTER)'

test-lua: $(addprefix run-lua-program-,$(LUA_PROGRAMS)) \
	$(addprefix run-lua-spec-,$(LUA_SPECS)) run-lua-spec-cm-cli test-evmu

# Convenience alias for running just the cartesi-machine CLI spec.
test-cm-cli: run-lua-spec-cm-cli

# The evmu spec exercises Lua-only code, so it runs under LUA_CLI to measure its
# coverage with luacov. Ordered after spec-cm-cli so their luacov.stats.out
# writes do not race (only relevant under coverage).
test-evmu: | $(TEST_EVMU_ORDER)
	$(LUA_CLI) lua/spec-evmu.lua --filter='$(SPEC_FILTER)'

test-c-api: | $(CARTESI_IMAGES)
	./build/misc/test-machine-c-api

test-c-api-remote: | $(CARTESI_IMAGES)
	CARTESI_JSONRPC_TEST_REMOTE=../src/cartesi-jsonrpc-machine ./build/misc/test-machine-c-api

test-c-jsonrpc-api: | $(CARTESI_IMAGES)
	CARTESI_JSONRPC_MACHINE=../src/cartesi-jsonrpc-machine ./build/misc/test-machine-c-jsonrpc-api

test-save-and-load: | $(CARTESI_IMAGES)
	./scripts/test-save-and-load.sh '$(LUA) ../src/cartesi-machine.lua'

test-yield-and-save: | $(CARTESI_IMAGES)
	./scripts/test-yield-and-save.sh '$(LUA) ../src/cartesi-machine.lua'

test-fuzz: build-tests-fuzz
	@$(MAKE) -C fuzz CC=clang CXX=clang++ fuzz=yes run-fuzz-interpret

test-misc: test-c-api test-c-api-remote test-c-jsonrpc-api test-save-and-load test-yield-and-save

test-generate-uarch-logs: $(BUILDDIR)/uarch-riscv-tests-json-logs
	$(LUA) ./lua/uarch-riscv-tests.lua --output-dir=$(BUILDDIR)/uarch-riscv-tests-json-logs --create-reset-uarch-log --create-send-cmio-response-log --jobs=$(NUM_JOBS) json-step-logs
	tar -czf uarch-riscv-tests-json-logs.tar.gz -C $(BUILDDIR) uarch-riscv-tests-json-logs

test-interactive:
	echo uname -a | $(LUA) ../src/cartesi-machine.lua --quiet --no-init-splash -it sh | grep -q "riscv64 GNU/Linux"
	echo uname -a | $(LUA) ../src/cartesi-machine.lua --quiet --no-init-splash -i sh | grep -q "riscv64 GNU/Linux"

test-workload:
	$(LUA) ../src/cartesi-machine.lua -- "$(COVERAGE_WORKLOAD)"

test-coverage-machine: test-interactive test-workload
	$(LUA) ../src/cartesi-machine.lua --max-mcycle=1

UARCH_PCS_DIR=$(BUILDDIR)/uarch-pcs
UARCH_COV_ELF=../uarch/uarch-ram-coverage.elf
UARCH_COV_BIN=../uarch/uarch-ram-coverage.bin

test-coverage-uarch:
	$(LUA) ./lua/cartesi-machine-tests.lua --test=".*csr.*" run_host_and_uarch
	$(LUA) ./lua/htif-yield.lua --uarch

# Tests that may run concurrently under make -j. Under coverage they share only
# the .gcda files, which libgcov merges under a lock. Excludes the two coverage
# tail steps ordered below.
PARALLEL_TESTS = test-save-and-load test-machine test-lua test-jsonrpc \
	test-c-api test-c-api-remote test-c-jsonrpc-api test-coverage-machine \
	test-uarch-rv64ui test-uarch-interpreter test-machine-with-log-step \
	test-coverage-uarch test-computation-hash

test-parallel: $(PARALLEL_TESTS)

# Relink the emulator so reset_uarch restores the instrumented uarch for the
# whole run, then collect the executed uarch PCs. This permanently swaps the
# embedded pristine in the build tree, so it must not overlap any test that
# executes the emulator. It is ordered after the whole parallel group.
test-coverage-uarch-pcs: | test-parallel
	mkdir -p $(UARCH_PCS_DIR)
	$(MAKE) -C ../src swap-uarch-pristine-coverage
	$(LUA) ./lua/cartesi-machine-tests.lua --test=".*csr.*" --uarch-pc-output-dir=$(UARCH_PCS_DIR) --jobs=$(NUM_JOBS) run_uarch_coverage
	$(LUA) ./lua/cartesi-machine-tests.lua --test="thrash%-tlb.bin" --uarch-pc-output-dir=$(UARCH_PCS_DIR) --jobs=$(NUM_JOBS) run_uarch_coverage

ifeq ($(COVERAGE_TOOLCHAIN),clang)
export LLVM_PROFILE_FILE=coverage-%p.profraw
endif

ifeq ($(COVERAGE_TOOLCHAIN),gcc)
GCOV=gcov
else ifeq ($(COVERAGE_TOOLCHAIN),clang)
GCOV=llvm-cov gcov

export LLVM_PROFILE_FILE=coverage-%p.profraw
endif

ifeq ($(DEV_ENV_HAS_TOOLCHAIN),yes)
UARCH_PCS_TO_GCOV_CMD=$(LUA) ./scripts/uarch-pcs-to-gcov.lua $(UARCH_COV_ELF) $(UARCH_PCS_DIR) ../src
else
UARCH_PCS_TO_GCOV_CMD=$(MAKE) toolchain-exec CONTAINER_COMMAND='/bin/bash -c "lua5.4 ./scripts/uarch-pcs-to-gcov.lua $(UARCH_COV_ELF) build/uarch-pcs ../src"'
endif

GCOVR_FLAGS=--gcov-ignore-parse-errors --use-gcov-files \
	--exclude-unreachable-branches --exclude-function-lines \
	--exclude-noncode-lines --exclude-throw-branches \
	--exclude-lines-by-pattern '\s*contract_assert.*'

coverage-report: $(COVERAGE_OUTPUT_DIR)
	$(LUA) ./scripts/run-gcov.lua $(MACHINE_EMULATOR_SRC_DIR) '$(GCOV)'
	$(UARCH_PCS_TO_GCOV_CMD)
	$(LUA) ./scripts/luacov-to-gcov.lua $(LUACOV_STATS) ../src/cartesi-machine.lua ../src
	$(LUA) ./scripts/luacov-to-gcov.lua $(LUACOV_STATS) ../src/cartesi/evmu.lua ../src
	mkdir -p $(COVERAGE_OUTPUT_DIR)/gcc
	cd ../src && gcovr $(GCOVR_FLAGS) --root . --filter '$(abspath ../src)' --filter '$(abspath ../uarch)' --html-details $(COVERAGE_OUTPUT_DIR)/gcc/index.html --txt $(COVERAGE_OUTPUT_DIR)/coverage.txt --print-summary

# Remove all collected coverage data so the next run starts from scratch.
# Otherwise .gcda counters, luacov stats, and uarch PC files accumulate across
# runs and inflate the report.
clean-coverage:
	@rm -f ../src/*.gcda ../src/*.gcov
	@rm -rf $(LUACOV_DIR) $(UARCH_PCS_DIR) $(COVERAGE_OUTPUT_DIR)

# Full coverage run. It runs the parallel test group, then the in-place pristine-swap
# uarch collection (both sequenced by order-only prerequisites), then the report. Run with
# -j. coverage-report itself carries no prerequisites so it can also be run alone to
# regenerate the report from existing coverage data. Ends by relinking the stock
# pristine so the build tree is ready for the next run.
coverage-all: | test-coverage-uarch-pcs
	$(MAKE) coverage-report
	$(MAKE) -C ../src swap-uarch-pristine-stock

test: test-save-and-load test-yield-and-save test-machine test-uarch test-uarch-rv64ui test-uarch-interpreter test-lua test-jsonrpc test-c-api test-cmio test-machine-with-log-step create-computation-hash-corpus

lint format check-format:
	@$(MAKE) -C misc $@

format-lua:
	@$(STYLUA) $(STYLUA_FLAGS) lua/

check-format-lua:
	@$(STYLUA) $(STYLUA_FLAGS) --check lua/

check-lua:
	luacheck lua/

.PHONY: machine uarch misc riscv-tests riscv-arch-test build-tests-fuzz build-tests-fuzz-seed-corpus test-fuzz
.PHONY: $(PARALLEL_TESTS) test-parallel test test-misc test-lua test-cm-cli test-evmu run-lua-spec-cm-cli \
	test-coverage-uarch-pcs coverage-report coverage-all clean-coverage
.PHONY: build-tests-computation-hash-fixtures build-computation-hash-corpus \
	build-computation-hash-corpus-with-toolchain create-computation-hash-corpus archive-computation-hash-corpus \
	test-computation-hash verify-existing-computation-hash-corpus clean-computation-hash-corpus
