From ba65be2d130d7a98beef2cc408de6be6dabbf78f Mon Sep 17 00:00:00 2001 From: Eddy Ashton Date: Fri, 6 Mar 2026 08:31:22 +0000 Subject: [PATCH] Pass CMake details to e2e tests via a configuration file --- CMakeLists.txt | 33 ++++++++++++++++++++++++++++----- cmake/common.cmake | 36 ++++++++++++++++++++---------------- cmake/e2e_config.json.in | 7 +++++++ tests/infra/e2e_args.py | 20 ++++++++++++++++++++ 4 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 cmake/e2e_config.json.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e15962f6bcd..526b2f140729 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,20 +283,43 @@ set(WORKER_THREADS CACHE STRING "Number of worker threads to start on each CCF node" ) -set(CCF_NETWORK_TEST_DEFAULT_CONSTITUTION - --constitution +# Default constitution file paths (used for both CLI args and config generation) +set(CCF_DEFAULT_CONSTITUTION_FILES ${CCF_DIR}/samples/constitutions/default/actions.js - --constitution ${CCF_DIR}/samples/constitutions/default/validate.js - --constitution ${CCF_DIR}/samples/constitutions/default/resolve.js - --constitution ${CCF_DIR}/samples/constitutions/default/apply.js ) + +# Build the --constitution CLI args from the file list +set(CCF_NETWORK_TEST_DEFAULT_CONSTITUTION "") +foreach(f ${CCF_DEFAULT_CONSTITUTION_FILES}) + list(APPEND CCF_NETWORK_TEST_DEFAULT_CONSTITUTION --constitution ${f}) +endforeach() + set(CCF_NETWORK_TEST_ARGS --log-level ${TEST_LOGGING_LEVEL} --worker-threads ${WORKER_THREADS} ) +# Tick period: faster for non-instrumented builds +if(SAN) + set(NODE_TICK_MS 10) +else() + set(NODE_TICK_MS 1) +endif() + +# Build JSON array of default constitution paths for e2e config +list(TRANSFORM CCF_DEFAULT_CONSTITUTION_FILES REPLACE "(.+)" "\"\\1\"" + OUTPUT_VARIABLE _quoted +) +list(JOIN _quoted ", " DEFAULT_CONSTITUTION_JSON) +set(DEFAULT_CONSTITUTION_JSON "[${DEFAULT_CONSTITUTION_JSON}]") + +# Generate e2e test config consumed by tests/infra/e2e_args.py +configure_file( + ${CCF_DIR}/cmake/e2e_config.json.in ${CMAKE_BINARY_DIR}/e2e_config.json @ONLY +) + # SNIPPET_START: JS generic application add_ccf_app( js_generic SRCS ${CCF_DIR}/src/apps/js_generic/js_generic.cpp diff --git a/cmake/common.cmake b/cmake/common.cmake index e6d77f9e5fdd..c9cba3304e3f 100644 --- a/cmake/common.cmake +++ b/cmake/common.cmake @@ -66,10 +66,6 @@ function(add_e2e_test) "CONSTITUTION;ADDITIONAL_ARGS;CONFIGURATIONS" ) - if(NOT PARSED_ARGS_CONSTITUTION) - set(PARSED_ARGS_CONSTITUTION ${CCF_NETWORK_TEST_DEFAULT_CONSTITUTION}) - endif() - if(BUILD_END_TO_END_TESTS) if(PROFILE_TESTS) set(PYTHON_WRAPPER @@ -86,24 +82,32 @@ function(add_e2e_test) set(PYTHON_WRAPPER ${PYTHON}) endif() - # For fast e2e runs, tick node faster than default value (except for - # instrumented builds which may process ticks slower). - if(SAN) - set(NODE_TICK_MS 10) - else() - set(NODE_TICK_MS 1) - endif() - if(NOT PARSED_ARGS_PERF_LABEL) set(PARSED_ARGS_PERF_LABEL ${PARSED_ARGS_NAME}) endif() + # Build the command line. Global defaults (binary_dir, log_level, + # worker_threads, tick_ms, default_constitution) are now provided by the + # e2e_config.json generated at configure time and consumed by e2e_args.py. + # Only test-specific args remain here. + set(E2E_CMD + ${PYTHON_WRAPPER} ${PARSED_ARGS_PYTHON_SCRIPT} --label + ${PARSED_ARGS_NAME} + ) + + # Constitution: only pass on CLI when explicitly overridden for this test. + # Otherwise e2e_args.py falls back to default_constitution from config. + if(PARSED_ARGS_CONSTITUTION) + list(APPEND E2E_CMD ${PARSED_ARGS_CONSTITUTION}) + endif() + + if(PARSED_ARGS_ADDITIONAL_ARGS) + list(APPEND E2E_CMD ${PARSED_ARGS_ADDITIONAL_ARGS}) + endif() + add_test( NAME ${PARSED_ARGS_NAME} - COMMAND - ${PYTHON_WRAPPER} ${PARSED_ARGS_PYTHON_SCRIPT} -b . --label - ${PARSED_ARGS_NAME} ${CCF_NETWORK_TEST_ARGS} ${PARSED_ARGS_CONSTITUTION} - ${PARSED_ARGS_ADDITIONAL_ARGS} --tick-ms ${NODE_TICK_MS} + COMMAND ${E2E_CMD} CONFIGURATIONS ${PARSED_ARGS_CONFIGURATIONS} ) diff --git a/cmake/e2e_config.json.in b/cmake/e2e_config.json.in new file mode 100644 index 000000000000..718dd488d1c5 --- /dev/null +++ b/cmake/e2e_config.json.in @@ -0,0 +1,7 @@ +{ + "binary_dir": "@CMAKE_BINARY_DIR@", + "log_level": "@TEST_LOGGING_LEVEL@", + "worker_threads": @WORKER_THREADS@, + "tick_ms": @NODE_TICK_MS@, + "default_constitution": @DEFAULT_CONSTITUTION_JSON@ +} diff --git a/tests/infra/e2e_args.py b/tests/infra/e2e_args.py index 7cbf6c1f4a9b..345e7992b794 100644 --- a/tests/infra/e2e_args.py +++ b/tests/infra/e2e_args.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the Apache 2.0 License. import argparse +import json import os import infra.interfaces import infra.path @@ -384,13 +385,32 @@ def cli_args( type=str, default=infra.clients.API_VERSION_01, ) + add(parser) + # Look for e2e_config.json in cwd (the build dir when run via ctest). + # Values from the config are applied as parser defaults so that explicit + # CLI args still take precedence. + e2e_config = None + config_path = os.path.join(os.getcwd(), "e2e_config.json") + if os.path.isfile(config_path): + with open(config_path, encoding="utf-8") as f: + e2e_config = json.load(f) + config_defaults = { + k: v for k, v in e2e_config.items() if k != "default_constitution" + } + parser.set_defaults(**config_defaults) + if accept_unknown: args, unknown_args = parser.parse_known_args() else: args = parser.parse_args() + # Constitution uses append action, so set_defaults can't help. + # Fall back to config value when nothing was passed on the CLI. + if e2e_config and not args.constitution: + args.constitution = e2e_config.get("default_constitution", []) + args.binary_dir = os.path.abspath(args.binary_dir) if args.library_dir is None: