# test/Makefile — ホストビルド

CXX      = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -I../src -I. -DARDUINO=0 -DAZARAC_LANG_JA=1 -DAZARAC_LANG_EN=1 -MMD -MP

# --- Static Analysis and Coverage ---
# -Wconversion / -Wsign-conversion はビット演算を多用する組み込み向けコードでは修正コストが
# 見合わないため使わない。-Wshadow は有効（実害のあるシャドウイングを検出するため）。
ANALYSIS_CXXFLAGS = $(CXXFLAGS) -Wpedantic -Wshadow -Wnull-dereference -Wdouble-promotion -Wformat=2 -Werror
COVERAGE_FLAGS    = -O0 -fprofile-arcs -ftest-coverage
# check / check-xml で共有する cppcheck オプション（ドリフト防止）。
# uninitMemberVarNoCtor は POD 集約体を memset + placement new で扱う設計
# （Mt43Data/Mt44Data の payload 共用体、Dedup.h のキー構造体）で常に誤検知になるため抑制する
# （実測 24 件はすべて手書き集約体と test/ のベンチ・ファズ構造体。生成定義テーブルには出ない）。
CPPCHECK_OPTS = --enable=warning,performance,portability --check-level=exhaustive --inline-suppr --suppress=missingIncludeSystem --suppress=*:doctest.h --suppress=uninitMemberVarNoCtor --error-exitcode=1 --std=c++17 -I../src -I. -DAZARAC_NANKAI_MAX_PAGES=63 ../src .

# MinGW-w64 bin path (opt-in; set on the command line if g++ is not on PATH,
# e.g. make MINGW64_BIN=C:\mingw64\bin)
MINGW64_BIN ?=

# OS detection
ifeq ($(OS),Windows_NT)
  TEST_BIN   = run_tests.exe
  RUN_PREFIX =
  RM         = rm -f
  ifneq ($(MINGW64_BIN),)
    ifneq ($(wildcard $(MINGW64_BIN)/g++.exe),)
      export PATH := $(MINGW64_BIN);$(PATH)
    endif
  endif
  # /dev/null fallback — Windows では NUL
  DEVNULL = NUL
  # メモリ割り当て増加: 256MBスタック + 512MBヒープ (MinGW)
  LDFLAGS = -Wl,--stack,268435456 -Wl,--heap,536870912
else
  TEST_BIN   = run_tests
  RUN_PREFIX = ./
  RM         = rm -f
  LDFLAGS =
  # /dev/null fallback — Linux/macOS では /dev/null
  DEVNULL = /dev/null
endif

# Optimization profile: make RELEASE=1 for -Os (smaller code, no debug symbols)
ifdef RELEASE
  CXXFLAGS += -Os -fdata-sections -ffunction-sections
  LDFLAGS += -Wl,--gc-sections
else
  CXXFLAGS += -g
endif

# Test sources (機能別ディレクトリ)
TEST_SRCS = \
	test_main.cpp \
	core/test_core.cpp \
	core/test_boundary_values.cpp \
	core/test_message_struct.cpp \
	core/test_macro_disabled.cpp \
	framer/test_framer.cpp \
	framer/test_ublox_azarashi.cpp \
	framer/test_ublox_multignss.cpp \
	internal/test_dcx_helper_b4.cpp \
	internal/test_dcx_helper.cpp \
	internal/test_avr_std_shim.cpp \
	internal/test_definition_labels.cpp \
	internal/test_dedup_window.cpp \
	json/test_json.cpp \
	json/test_json_dcx_b1b4.cpp \
	json/test_json_wellformed.cpp \
	integration/test_parser.cpp \
	integration/test_parser_integration.cpp \
	integration/test_nankai.cpp \
	integration/test_nankai_e2e.cpp \
	integration/test_error_handling.cpp \
	integration/test_time_resolution.cpp \
	integration/test_dcx_edge_cases.cpp \
	integration/test_azarashi_dcr.cpp \
	integration/test_azarashi_dcx.cpp \
	integration/test_realdata.cpp

# Library sources (src/ 配下の実装ファイル)
LIB_SRCS = \
	../src/Parser.cpp \
	../src/decoder/Decoder.cpp \
	../src/decoder/DecoderDcx.cpp \
	../src/decoder/DecoderQzqsm.cpp \
	../src/framer/UbxFramer.cpp \
	../src/framer/NmeaFramer.cpp \
	../src/json/JsonWriter.cpp \
	../src/json/JsonSerializer.cpp \
	../src/json/JsonSerializerDcx.cpp \
	../src/json/JsonSerializerQzqsm.cpp \
	../src/internal/DcxHelper.cpp \
	../src/internal/Dedup.cpp

# Object files - サブディレクトリの.oはそのまま保持、ライブラリはカレントに
TEST_OBJS = $(TEST_SRCS:.cpp=.o)
LIB_OBJS = $(notdir $(LIB_SRCS:.cpp=.o))
OBJS = $(TEST_OBJS) $(LIB_OBJS)

# VPATH for library sources
VPATH = ../src:../src/decoder:../src/framer:../src/json:../src/internal

.PHONY: all clean run run-small-pages run-lang-defaults fuzz decode dedup-realday bench compare-realdata check check-xml check-strict-compile coverage test-no-alloc pgm-stub

all: $(TEST_BIN)

# Rule to build the test binary
$(TEST_BIN): $(OBJS)
	$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) $^ -o $@ $(LDFLAGS)

# Pattern rules for test sources (サブディレクトリ対応)
%.o: %.cpp
	$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) -c $< -o $@

run: all
	$(RUN_PREFIX)$(TEST_BIN)

# 縮小バジェット経路の検証（既定は 63 = 仕様最大）。打ち切りを含む MAX_PAGES
# 依存の分岐を通すため -B で全体を再ビルドする。CXXFLAGS_EXTRA は依存関係に
# 載らないため、-B で作った .o を残すと既定構成と混在して以降の run が
# 再ビルドされない（= 縮小構成のまま「既定を検証した」と誤認する）か、
# check-strict-compile 後に混在リンクして落ちる。必ず clean する。
run-small-pages:
	$(MAKE) -B CXXFLAGS_EXTRA="-DAZARAC_NANKAI_MAX_PAGES=8" run; status=$$?; $(MAKE) clean; exit $$status

# 言語構成の検証。ライブラリ既定は JA=1 / EN=0 で、テスト既定（JA=1 / EN=1）とは
# 出力が変わる: 日本語版を持たない英語専用表が EN=0 で消える回帰は、テスト既定では
# 一切現れない（テスト既定では EN 表が生きているため）。両方を回して初めて
# 「既定構成でラベルが消える」型のバグを検出できる。
run-lang-defaults:
	$(MAKE) -B CXXFLAGS_EXTRA="-DAZARAC_LANG_JA=0 -DAZARAC_LANG_EN=1" run; s1=$$?; \
	$(MAKE) -B CXXFLAGS_EXTRA="-DAZARAC_LANG_JA=1 -DAZARAC_LANG_EN=0" run; s2=$$?; \
	$(MAKE) clean; [ $$s1 -eq 0 ] && [ $$s2 -eq 0 ]

# Fuzz target — FUZZ_ITERATIONS をargv[1]として渡す (argv[1] > getenv > default)
FUZZ_BIN = fuzz_decoder
FUZZ_SRCS = fuzz/fuzz_decoder.cpp
FUZZ_OBJS = $(FUZZ_SRCS:.cpp=.o)
FUZZ_ITERATIONS ?= 10000

fuzz: $(FUZZ_BIN)
	$(RUN_PREFIX)$(FUZZ_BIN) $(FUZZ_ITERATIONS)

$(FUZZ_BIN): $(FUZZ_OBJS) $(LIB_OBJS)
	$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) $^ -o $@ $(LDFLAGS)

# Decode-to-JSON CLI tool (for azarashi comparison)
DECODE_BIN = decode_to_json
DECODE_SRCS = tools/decode_to_json.cpp
DECODE_OBJS = $(DECODE_SRCS:.cpp=.o)

decode: $(DECODE_BIN)

$(DECODE_BIN): $(DECODE_OBJS) $(LIB_OBJS)
	$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) $^ -o $@ $(LDFLAGS)

# Compare with azarashi (requires: pip install azarashi)
PYTHON ?= python
COMPARE_ARGS ?=
compare-realdata: $(DECODE_BIN)
	$(PYTHON) scripts/compare_with_azarashi.py $(COMPARE_ARGS)

# Real-day reproduction of the duplicate filter's error rate (default capacity).
# realdata/ is .gitignore'd and absent from CI, so the input is optional.
DEDUP_REALDAY_BIN  = dedup_realday
DEDUP_REALDAY_SRCS = tools/dedup_realday.cpp
DEDUP_REALDAY_OBJS = $(DEDUP_REALDAY_SRCS:.cpp=.o)
REALDAY_INPUT ?= ../realdata/Q002_20240101.l1s

dedup-realday: $(DEDUP_REALDAY_BIN)
ifeq ($(wildcard $(REALDAY_INPUT)),)
	@echo "dedup-realday: SKIP - $(REALDAY_INPUT) not found"
else
	$(RUN_PREFIX)$(DEDUP_REALDAY_BIN) $(REALDAY_INPUT)
endif

$(DEDUP_REALDAY_BIN): $(DEDUP_REALDAY_OBJS) $(LIB_OBJS)
	$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) $^ -o $@ $(LDFLAGS)

# dedup ベンチ（autoresearch.sh）。入力は test/data/*.json のみで realdata は不要。
bench:
	@bash ../autoresearch.sh

# --- Static Analysis ---
# 静的解析 (時間をかけた厳格なチェック)
check:
	@echo "Running cppcheck..."
	cppcheck $(CPPCHECK_OPTS)
	@echo "Compiling library with strict warnings..."
	$(CXX) $(ANALYSIS_CXXFLAGS) -c $(LIB_SRCS)

# cppcheck の XML レポートをファイルに保存（サマリー生成用）。
# --error-exitcode=1 で所見があれば失敗させる（CI ゲート）。
# uninitMemberVarNoCtor は集約構造体・自動生成定義テーブル由来の誤検知なので抑制。
check-xml:
	@echo "Running cppcheck (XML output)..."
	cppcheck --xml --xml-version=2 --output-file=cppcheck_report.xml $(CPPCHECK_OPTS) 2> cppcheck_stderr.log
	@echo "cppcheck XML report saved to cppcheck_report.xml"

# 厳格コンパイルチェックのみ（check から cppcheck を除いた部分）
check-strict-compile:
	@echo "Compiling library with strict warnings..."
	$(CXX) $(ANALYSIS_CXXFLAGS) -c $(LIB_SRCS)

# --- Coverage ---
# カバレッジ測定
coverage:
	@$(MAKE) clean
	@echo "Building with coverage instrumentation..."
	$(MAKE) CXXFLAGS="$(CXXFLAGS) $(COVERAGE_FLAGS)" $(TEST_BIN)
	@echo "Running tests with coverage..."
	$(RUN_PREFIX)$(TEST_BIN)
	@echo "----------------------------------------"
	gcovr . --filter=../src/ --exclude='.*test_.*\.cpp' --exclude='.*definition.*' --txt

# --- 動的確保ゼロ検証 (test-no-alloc) ---
# GNU ld の --wrap は未解決参照しか差し替えないため、ライブラリとテストを 1 つの
# 実行ファイルにリンクする。--wrap はリンク時にしか効かないので、通常の
# run_tests とは別バイナリ・別オブジェクトディレクトリ (.noalloc/) を使う
# （既定の .o と混在させると __real_* 未定義でリンクが落ちる）。
# CXXFLAGS_EXTRA は依存関係に載らないため、実行後は clean する（残った
# .noalloc/*.o が縮小構成のまま再利用されるのを避ける）。
# macOS の ld64 は --wrap を持たないためスキップする（uname -s は MinGW で
# MSYS_NT-* を返すので Darwin 判定で問題ない）。
NOALLOC_BIN = no_alloc_tests$(suffix $(TEST_BIN))
NOALLOC_TMP = .noalloc
NOALLOC_WRAP = -Wl,--wrap=malloc -Wl,--wrap=calloc -Wl,--wrap=realloc -Wl,--wrap=posix_memalign
NOALLOC_OBJS = $(addprefix $(NOALLOC_TMP)/,$(notdir $(LIB_SRCS:.cpp=.o))) $(NOALLOC_TMP)/test_no_alloc.o

ifeq ($(shell uname -s),Darwin)
test-no-alloc:
	@echo "test-no-alloc: SKIP — GNU ld (--wrap) is required; macOS ld64 cannot provide it."
else
test-no-alloc:
	$(MAKE) $(NOALLOC_BIN); b=$$?; if [ $$b -ne 0 ]; then $(MAKE) clean; exit $$b; fi; \
	./$(NOALLOC_BIN); t=$$?; $(MAKE) clean; exit $$t

$(NOALLOC_TMP):
	@mkdir -p $@

# ディレクトリは order-only にする（通常の prerequisite にすると mtime 変化で
# 全 .o が毎回再ビルドされる）。
$(NOALLOC_BIN): $(NOALLOC_OBJS) | $(NOALLOC_TMP)
	$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) $^ -o $@ $(LDFLAGS) $(NOALLOC_WRAP)

$(NOALLOC_OBJS): | $(NOALLOC_TMP)

# テスト本体はサブディレクトリにあり VPATH（../src 配下）に無いためパターン
# ルールでは解決できない。明示ルールを置く。
$(NOALLOC_TMP)/test_no_alloc.o: integration/test_no_alloc.cpp
	$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) -c $< -o $@

$(NOALLOC_TMP)/%.o: %.cpp
	$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) -c $< -o $@
endif
# --- AZARAC_ENABLE_* Macro Tests ---
# 全マクロ無効化でもコンパイルが通ることを検証する
MACRO_OFF_FLAGS = \
	-DAZARAC_ENABLE_EEW=0 -DAZARAC_ENABLE_HYPOCENTER=0 \
	-DAZARAC_ENABLE_SEISMIC=0 -DAZARAC_ENABLE_NANKAI=0 \
	-DAZARAC_ENABLE_TSUNAMI=0 -DAZARAC_ENABLE_NW_PAC_TSUNAMI=0 \
	-DAZARAC_ENABLE_VOLCANO=0 -DAZARAC_ENABLE_ASH_FALL=0 \
	-DAZARAC_ENABLE_WEATHER=0 -DAZARAC_ENABLE_FLOOD=0 \
	-DAZARAC_ENABLE_TYPHOON=0 -DAZARAC_ENABLE_MARINE=0 \
	-DAZARAC_ENABLE_DCX_CAMF=0

.PHONY: test-macro-compile test-macro-sizes

test-macro-compile:
	@echo "=== Compiling with ALL AZARAC_ENABLE_* macros disabled ==="
	@mkdir -p $(MACRO_TMP)
	@set -e; for f in $(LIB_SRCS); do bn=$$(basename $$f); $(CXX) $(CXXFLAGS) $(MACRO_OFF_FLAGS) -c $$f -o $(MACRO_TMP)/$$bn.o; done
	@rm -rf $(MACRO_TMP)
	@echo "===== SUCCESS: all macros disabled compilation passed ====="

MACRO_TMP = .macro_tmp
QZQSM_SRC = ../src/decoder/DecoderQzqsm.cpp
DCX_SRC   = ../src/decoder/DecoderDcx.cpp
JSON_SRC  = ../src/json/JsonSerializerQzqsm.cpp
JSON_DCX_SRC = ../src/json/JsonSerializerDcx.cpp

# ---- Macro size impact measurement (Unix/Linux only; requires sh + size) ----
ifneq ($(OS),Windows_NT)
test-macro-sizes:
	@echo "=== AZARAC_ENABLE_* Macro Size Impact ==="
	@echo ""
	@echo "Building baseline (all macros ON)..."
	@mkdir -p $(MACRO_TMP)
	$(CXX) $(CXXFLAGS) -c $(QZQSM_SRC) -o $(MACRO_TMP)/bl_qzqsm.o
	$(CXX) $(CXXFLAGS) -c $(DCX_SRC) -o $(MACRO_TMP)/bl_dcx.o
	$(CXX) $(CXXFLAGS) -c $(JSON_SRC) -o $(MACRO_TMP)/bl_json.o
	$(CXX) $(CXXFLAGS) -c $(JSON_DCX_SRC) -o $(MACRO_TMP)/bl_json_dcx.o
	@echo ""
	@echo "=== Size report (.text bytes; host g++ -O0, relative comparison) ==="
	@echo "| Macro disabled | DecoderQzqsm | DecoderDcx | JsonSerQzqsm | JsonSerDcx |"
	@echo "|---------------|-------------|------------|-------------|-----------|"
	@BL_QZQSM=`size $(MACRO_TMP)/bl_qzqsm.o 2>/dev/null | tail -1 | awk '{print $$1}'`; \
	BL_DCX=`size $(MACRO_TMP)/bl_dcx.o 2>/dev/null | tail -1 | awk '{print $$1}'`; \
	BL_JSON=`size $(MACRO_TMP)/bl_json.o 2>/dev/null | tail -1 | awk '{print $$1}'`; \
	BL_JSON_DCX=`size $(MACRO_TMP)/bl_json_dcx.o 2>/dev/null | tail -1 | awk '{print $$1}'`; \
	printf "| %-14s | %-11s | %-10s | %-11s | %-9s |\n" "BASELINE (all ON)" "$$BL_QZQSM" "$$BL_DCX" "$$BL_JSON" "$$BL_JSON_DCX"; \
	set -e; \
	for m in EEW HYPOCENTER SEISMIC NANKAI TSUNAMI NW_PAC_TSUNAMI VOLCANO ASH_FALL WEATHER FLOOD TYPHOON MARINE; do \
		$(CXX) $(CXXFLAGS) -DAZARAC_ENABLE_$$m=0 -c $(QZQSM_SRC) -o $(MACRO_TMP)/$$m.o; \
		qz=`size $(MACRO_TMP)/$$m.o 2>/dev/null | tail -1 | awk '{print $$1}'`; \
		diff_qz=$$((BL_QZQSM - qz)); \
		$(CXX) $(CXXFLAGS) -DAZARAC_ENABLE_$$m=0 -c $(JSON_SRC) -o $(MACRO_TMP)/j$$m.o; \
		js=`size $(MACRO_TMP)/j$$m.o 2>/dev/null | tail -1 | awk '{print $$1}'`; \
		diff_js=$$((BL_JSON - js)); \
		printf "| %-14s | %-5s(-%4s) | %-10s | %-5s(-%5s) | %-9s |\n" "ENABLE_$${m}=0" "$$qz" "$$diff_qz" "---" "$$js" "$$diff_js" "---"; \
	done; \
	$(CXX) $(CXXFLAGS) -DAZARAC_ENABLE_DCX_CAMF=0 -c $(DCX_SRC) -o $(MACRO_TMP)/dcx_off.o; \
	$(CXX) $(CXXFLAGS) -DAZARAC_ENABLE_DCX_CAMF=0 -c $(JSON_DCX_SRC) -o $(MACRO_TMP)/json_dcx_off.o; \
	dcx=`size $(MACRO_TMP)/dcx_off.o 2>/dev/null | tail -1 | awk '{print $$1}'`; \
	json_dcx=`size $(MACRO_TMP)/json_dcx_off.o 2>/dev/null | tail -1 | awk '{print $$1}'`; \
	diff_dcx=$$((BL_DCX - dcx)); \
	diff_json_dcx=$$((BL_JSON_DCX - json_dcx)); \
	printf "| %-14s | %-11s | %-5s(-%4s) | %-11s | %-5s(-%4s) |\n" "ENABLE_DCX_CAMF=0" "---" "$$dcx" "$$diff_dcx" "---" "$$json_dcx" "$$diff_json_dcx"
	@echo "|---------------|-------------|------------|-------------|-----------|"
	@echo "===== Measurement complete ====="
	@rm -rf $(MACRO_TMP)
else
# Windows fallback: just compile and show raw size output
test-macro-sizes:
	@echo "=== AZARAC_ENABLE_* Macro Size Impact (Windows, raw sizes) ==="
	@if not exist $(MACRO_TMP) mkdir $(MACRO_TMP)
	$(CXX) $(CXXFLAGS) -c $(QZQSM_SRC) -o $(MACRO_TMP)/bl_qzqsm.o
	$(CXX) $(CXXFLAGS) -c $(DCX_SRC) -o $(MACRO_TMP)/bl_dcx.o
	$(CXX) $(CXXFLAGS) -c $(JSON_SRC) -o $(MACRO_TMP)/bl_json.o
	$(CXX) $(CXXFLAGS) -c $(JSON_DCX_SRC) -o $(MACRO_TMP)/bl_json_dcx.o
	@echo "Baseline (all ON):"
	-@size $(MACRO_TMP)/bl_qzqsm.o
	-@size $(MACRO_TMP)/bl_dcx.o
	-@size $(MACRO_TMP)/bl_json.o
	-@size $(MACRO_TMP)/bl_json_dcx.o
	@echo "Individual macros disabled (compiling & sizing):"
	-@for %%m in (EEW HYPOCENTER SEISMIC NANKAI TSUNAMI NW_PAC_TSUNAMI VOLCANO ASH_FALL WEATHER FLOOD TYPHOON MARINE) do @( \
		$(CXX) $(CXXFLAGS) -DAZARAC_ENABLE_%%m=0 -c $(QZQSM_SRC) -o $(MACRO_TMP)/%%m.o 2>nul & \
		echo DecoderQzqsm with %%%%m=0: & \
		size $(MACRO_TMP)/%%m.o )
	-@$(CXX) $(CXXFLAGS) -DAZARAC_ENABLE_DCX_CAMF=0 -c $(DCX_SRC) -o $(MACRO_TMP)/dcx_off.o 2>nul
	-@echo DecoderDcx with DCX_CAMF=0: & size $(MACRO_TMP)/dcx_off.o
	-@$(CXX) $(CXXFLAGS) -DAZARAC_ENABLE_DCX_CAMF=0 -c $(JSON_DCX_SRC) -o $(MACRO_TMP)/json_dcx_off.o 2>nul
	-@echo JsonSerializerDcx with DCX_CAMF=0: & size $(MACRO_TMP)/json_dcx_off.o
	@echo "===== Measurement complete ====="
	@-rmdir /s /q $(MACRO_TMP) 2>nul
endif

# ---- Macro disable runtime tests (compile + run for each disabled macro) ----
# 各マクロを個別に無効化してビルド・実行し、無効化されたデコーダが
# 正しくエラー（unsupported_reason = DisabledAtCompileTime）を返すことを検証する。
# 全 13 マクロを個別にテストする（カバレッジ優先）。

MACRO_NAMES = EEW HYPOCENTER SEISMIC NANKAI TSUNAMI NW_PAC_TSUNAMI VOLCANO ASH_FALL WEATHER FLOOD TYPHOON MARINE DCX_CAMF

.PHONY: macro

# Individual macro disabled test: clean → rebuild with macro=0 → run
# 各マクロの無効化ビルドは独立して行う（clean → build → run）
ifeq ($(OS),Windows_NT)
macro:
	@echo === AZARAC_ENABLE_* Macro Runtime Tests ===
	@echo Testing all $(words $(MACRO_NAMES)) macros individually...
	@$(MAKE) _macro-test-one M=EEW
	@$(MAKE) _macro-test-one M=HYPOCENTER
	@$(MAKE) _macro-test-one M=SEISMIC
	@$(MAKE) _macro-test-one M=NANKAI
	@$(MAKE) _macro-test-one M=TSUNAMI
	@$(MAKE) _macro-test-one M=NW_PAC_TSUNAMI
	@$(MAKE) _macro-test-one M=VOLCANO
	@$(MAKE) _macro-test-one M=ASH_FALL
	@$(MAKE) _macro-test-one M=WEATHER
	@$(MAKE) _macro-test-one M=FLOOD
	@$(MAKE) _macro-test-one M=TYPHOON
	@$(MAKE) _macro-test-one M=MARINE
	@$(MAKE) _macro-test-one M=DCX_CAMF
	@$(MAKE) clean 2>$(DEVNULL)
	@echo ===== All macro runtime tests passed =====

.PHONY: _macro-test-one

_macro-test-one:
	@echo Testing AZARAC_ENABLE_$(M)=0...
	@$(MAKE) clean 2>$(DEVNULL)
	@$(MAKE) -B CXXFLAGS="$(CXXFLAGS) -DAZARAC_ENABLE_$(M)=0" run
else
macro:
	@echo "=== AZARAC_ENABLE_* Macro Runtime Tests ==="
	@echo "Testing all $(words $(MACRO_NAMES)) macros individually..."
	@for m in $(MACRO_NAMES); do \
		echo ""; \
		echo "--- Testing AZARAC_ENABLE_$${m}=0 ---"; \
		$(MAKE) clean 2>$(DEVNULL); \
		$(MAKE) CXXFLAGS="$(CXXFLAGS) -DAZARAC_ENABLE_$${m}=0" run || exit 1; \
	done
	@$(MAKE) clean 2>$(DEVNULL)
	@echo "===== All macro runtime tests passed ====="
endif

# Include dependency files
-include $(TEST_OBJS:.o=.d) $(LIB_OBJS:.o=.d) $(FUZZ_OBJS:.o=.d) $(DECODE_OBJS:.o=.d) $(DEDUP_REALDAY_OBJS:.o=.d)

clean:
	-@$(RM) *.o *.exe *.d *.gcno *.gcda cppcheck_report.xml cppcheck_stderr.log coverage.xml coverage_report.json macro_sizes.txt
	-@$(RM) $(NOALLOC_BIN)
	-@rm -rf $(NOALLOC_TMP)
	-@$(RM) core/*.o core/*.d core/*.gcno core/*.gcda
	-@$(RM) decoder/*.o decoder/*.d decoder/*.gcno decoder/*.gcda
	-@$(RM) framer/*.o framer/*.d framer/*.gcno framer/*.gcda
	-@$(RM) json/*.o json/*.d json/*.gcno json/*.gcda
	-@$(RM) integration/*.o integration/*.d integration/*.gcno integration/*.gcda
	-@$(RM) internal/*.o internal/*.d internal/*.gcno internal/*.gcda
	-@$(RM) fuzz/*.o fuzz/*.d fuzz/*.gcno fuzz/*.gcda
	-@$(RM) tools/*.o tools/*.d tools/*.gcno tools/*.gcda
	-@$(RM) scripts/*.o scripts/*.d scripts/*.gcno scripts/*.gcda

# ---- PROGMEM stub (AVR branch semantics test on host) ----
# Compiles all tests with __AVR__ defined and a stub avr/pgmspace.h so the
# PROGMEM code paths (memcpy_P / pgm_read_dword) exercise on the host.
# AVR presets that conflict with test assumptions (BUFFERS=1) are neutralized:
# SLOTS/WAYS are pinned to values the capacity tests can read. These must match
# the AVR preset (64 x 8): the previous SLOTS=8 combined with the AVR WAYS=8
# produced SETS=1, so the tests exercised no associativity and no eviction at all.
# 実行後は clean する（-D 群が依存関係に載らず、残った .o が
# 既定構成の .o と混在して次の `make run` が再ビルドされない／混在リンクで
# 落ちるため）。実行行は `;` を含むため必ずシェル経由になり、シェルは cwd を
# 探索しないので `./` を明示する（RUN_PREFIX は空か `./` のため使わない）。
# ビルドとテスト実行は 1 シェルにまとめる（別レシピ行だと Make がビルド失敗で
# 中断し clean が走らないため）。ビルド失敗時は clean 後にその終了コードを、
# 成功時はテストの終了コードを返す。
pgm-stub:
	$(MAKE) clean
	$(MAKE) CXXFLAGS="-std=c++17 -Wall -Wextra -Istub -I../src -I. -DARDUINO=0 -D__AVR__ -DAZARAC_AVR_STUB -DAZARAC_LANG_JA=1 -DAZARAC_LANG_EN=1 -DAZARAC_NANKAI_MAX_PAGES=63 -DAZARAC_NANKAI_BUFFERS=4 -DAZARAC_DEDUP_SLOTS=64 -DAZARAC_ENABLE_EEW=1 -DAZARAC_ENABLE_HYPOCENTER=1 -DAZARAC_ENABLE_SEISMIC=1 -DAZARAC_ENABLE_NANKAI=1 -DAZARAC_ENABLE_TSUNAMI=1 -DAZARAC_ENABLE_NW_PAC_TSUNAMI=1 -DAZARAC_ENABLE_VOLCANO=1 -DAZARAC_ENABLE_ASH_FALL=1 -DAZARAC_ENABLE_WEATHER=1 -DAZARAC_ENABLE_FLOOD=1 -DAZARAC_ENABLE_TYPHOON=1 -DAZARAC_ENABLE_MARINE=1 -DAZARAC_ENABLE_DCX_CAMF=1 -MMD -MP -g" $(TEST_BIN); b=$$?; if [ $$b -ne 0 ]; then $(MAKE) clean; exit $$b; fi; ./$(TEST_BIN); t=$$?; $(MAKE) clean; exit $$t
