diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a1b7250fa..4a2e33a63 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -69,3 +69,28 @@ endif() include(GoogleTest) gtest_discover_tests(vulkan_tools_tests DISCOVERY_TIMEOUT 100) + +# vulkaninfo command line parsing: output format flags are mutually exclusive. +# Argument parsing happens before any Vulkan call, so these tests do not need a driver. +if (TARGET vulkaninfo) + set(VULKANINFO_CONFLICTING_FORMATS + "--html|--json" + "--json|--html" + "--text|--html" + "--summary|--text" + "--summary|-j" + "--json=0|--summary" + "--vkconfig_output|--html" + ) + # Pairs use '|' as the separator since CMake flattens nested ';' lists + foreach(FORMAT_PAIR_STRING IN LISTS VULKANINFO_CONFLICTING_FORMATS) + string(REPLACE "|" ";" FORMAT_PAIR "${FORMAT_PAIR_STRING}") + string(REPLACE "|" "_" TEST_SUFFIX "${FORMAT_PAIR_STRING}") + string(REGEX REPLACE "[-=]" "" TEST_SUFFIX "${TEST_SUFFIX}") + set(TEST_NAME vulkaninfo_rejects_conflicting_formats_${TEST_SUFFIX}) + add_test(NAME ${TEST_NAME} COMMAND $ ${FORMAT_PAIR}) + set_tests_properties(${TEST_NAME} PROPERTIES + PASS_REGULAR_EXPRESSION "Only one output format may be specified" + ) + endforeach() +endif() diff --git a/vulkaninfo/vulkaninfo.cpp b/vulkaninfo/vulkaninfo.cpp index df7ce022b..3d9fc5423 100644 --- a/vulkaninfo/vulkaninfo.cpp +++ b/vulkaninfo/vulkaninfo.cpp @@ -1249,6 +1249,8 @@ enum class OutputCategory { text, html, profile_json, vkconfig_output, summary } const char *help_message_body = "OPTIONS:\n" "[-h, --help] Print this help.\n" + "Note: --summary, --text, --html, --json, and --vkconfig_output select the output\n" + " format and are mutually exclusive; specify at most one.\n" "[--summary] Show a summary of the instance and GPU's on a system.\n" "[-o , --output ]\n" " Print output to a new file whose name is specified by filename.\n" @@ -1315,12 +1317,18 @@ struct ParsedResults { util::vulkaninfo_optional parse_arguments(int argc, char **argv, std::string executable_name) { ParsedResults results{}; results.default_filename = APP_SHORT_NAME ".txt"; + // Output formats are mutually exclusive, so record every format requested and reject more than one distinct format. + std::set requested_categories; + auto set_output_category = [&](OutputCategory category) { + results.output_category = category; + requested_categories.insert(category); + }; for (int i = 1; i < argc; ++i) { // A internal-use-only format for communication with the Vulkan Configurator tool // Usage "--vkconfig_output " // -o can be used to specify the filename instead if (0 == strcmp("--vkconfig_output", argv[i])) { - results.output_category = OutputCategory::vkconfig_output; + set_output_category(OutputCategory::vkconfig_output); results.print_to_file = true; results.default_filename = APP_SHORT_NAME ".json"; if (argc > (i + 1) && argv[i + 1][0] != '-') { @@ -1332,6 +1340,7 @@ util::vulkaninfo_optional parse_arguments(int argc, char **argv, ++i; } } else if (strncmp("--json", argv[i], 6) == 0 || strncmp(argv[i], "-j", 2) == 0) { + set_output_category(OutputCategory::profile_json); if (strlen(argv[i]) > 7 && strncmp("--json=", argv[i], 7) == 0) { results.selected_gpu = static_cast(strtol(argv[i] + 7, nullptr, 10)); results.has_selected_gpu = true; @@ -1340,16 +1349,15 @@ util::vulkaninfo_optional parse_arguments(int argc, char **argv, results.selected_gpu = static_cast(strtol(argv[i] + 3, nullptr, 10)); results.has_selected_gpu = true; } - results.output_category = OutputCategory::profile_json; results.default_filename = APP_SHORT_NAME ".json"; results.print_to_file = true; } else if (strcmp(argv[i], "--summary") == 0) { - results.output_category = OutputCategory::summary; + set_output_category(OutputCategory::summary); } else if (strcmp(argv[i], "--text") == 0) { - results.output_category = OutputCategory::text; + set_output_category(OutputCategory::text); results.default_filename = APP_SHORT_NAME ".txt"; } else if (strcmp(argv[i], "--html") == 0) { - results.output_category = OutputCategory::html; + set_output_category(OutputCategory::html); results.print_to_file = true; results.default_filename = APP_SHORT_NAME ".html"; } else if (strcmp(argv[i], "--show-all") == 0) { @@ -1382,6 +1390,11 @@ util::vulkaninfo_optional parse_arguments(int argc, char **argv, return {}; } } + if (requested_categories.size() > 1) { + std::cerr << "Error: --summary, --text, --html, --json, and --vkconfig_output cannot be combined. " + "Only one output format may be specified.\n"; + return {}; + } return results; } diff --git a/vulkaninfo/vulkaninfo.md b/vulkaninfo/vulkaninfo.md index 17b4a8b69..686d37dce 100644 --- a/vulkaninfo/vulkaninfo.md +++ b/vulkaninfo/vulkaninfo.md @@ -61,6 +61,8 @@ USAGE: OPTIONS: [-h, --help] Print this help. +Note: --summary, --text, --html, and --json select the output format and are + mutually exclusive; specify at most one. [--summary] Show a summary of the instance and GPU's on a system. [-o , --output ] Print output to a new file whose name is specified by filename.