diff --git a/CMakeLists.txt b/CMakeLists.txt index 29f35fb..3b17585 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.14) project(armor) +set(TOOL_VERSION "0.6.5") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -11,13 +12,19 @@ execute_process( OUTPUT_STRIP_TRAILING_WHITESPACE ) -set(CLANG_EXTRA_FLAGS +set(CLANG_EXTRA_FLAGS_CPP "-std=c++17 -xc++ -isystem /usr/include/c++/11 -isystem /usr/include -isystem /usr/local/include -isystem /usr/include/x86_64-linux-gnu/c++/11 -I/usr/lib/llvm-14/include -I/usr/lib/clang/14/include" ) -string(REPLACE "\"" "\\\"" CLANG_EXTRA_FLAGS_ESCAPED "${CLANG_EXTRA_FLAGS}") +set(CLANG_EXTRA_FLAGS_C + "-xc -isystem /usr/include -isystem /usr/local/include -isystem /usr/include/x86_64-linux-gnu -I/usr/lib/llvm-14/include -I/usr/lib/clang/14/include" +) + +string(REPLACE "\"" "\\\"" CLANG_EXTRA_FLAGS_CPP_ESCAPED "${CLANG_EXTRA_FLAGS_CPP}") +string(REPLACE "\"" "\\\"" CLANG_EXTRA_FLAGS_C_ESCAPED "${CLANG_EXTRA_FLAGS_C}") -add_compile_definitions(CLANG_FLAGS="${CLANG_EXTRA_FLAGS_ESCAPED}") +add_compile_definitions(CLANG_FLAGS_CPP="${CLANG_EXTRA_FLAGS_CPP_ESCAPED}") +add_compile_definitions(CLANG_FLAGS_C="${CLANG_EXTRA_FLAGS_C_ESCAPED}") find_package(LLVM REQUIRED CONFIG) find_package(Clang REQUIRED CONFIG) @@ -38,7 +45,7 @@ llvm_map_components_to_libnames(LLVM_LIBS option ) -add_definitions(-DTOOL_VERSION="0.6.3") +add_definitions(-DTOOL_VERSION="${TOOL_VERSION}") add_subdirectory(src/common) add_subdirectory(src/alpha) diff --git a/README.md b/README.md index 8e47ff6..9e61e3e 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,10 @@ The tool is invoked directly using the `armor` binary: Report format: `html` (default). If `json` is provided, both HTML and JSON reports will be generated. +* **-l, --lang TEXT:{c,cpp}** + Language mode: `cpp` (default) or `c`. + Use `c` for C headers, `cpp` for C++ headers. + * **--dump-ast-diff** Dump AST diff JSON files for debugging @@ -136,7 +140,7 @@ The tool is invoked directly using the `armor` binary: 2. **Comparison with relative header paths:** ```bash - ./build/src/armor/armor /path/to/old/project /path/to/new/project include/api/foo.h include/api/bar.h + ./build/src/armor/armor /path/to/old/project /path/to/new/project include/api/foo.h ``` 3. **Generate JSON report with include paths:** @@ -159,6 +163,14 @@ The tool is invoked directly using the `armor` binary: foo.h ``` +5. **Comparing C headers:** + ```bash + ./build/src/armor/armor /path/to/old/project /path/to/new/project \ + --header-dir include \ + --lang c \ + foo.h + ``` + Test suite ---------- diff --git a/src/alpha/include/astnormalizer.hpp b/src/alpha/include/astnormalizer.hpp index 08901b1..8b9f219 100644 --- a/src/alpha/include/astnormalizer.hpp +++ b/src/alpha/include/astnormalizer.hpp @@ -25,6 +25,7 @@ class ASTNormalize : public clang::RecursiveASTVisitor { ASTNormalize(alpha::APISession* session, alpha::ASTNormalizedContext* context, clang::ASTContext* clangContext); bool TraverseNamespaceDecl(clang::NamespaceDecl *Decl); + bool TraverseRecordDecl(clang::RecordDecl *Decl); bool TraverseCXXRecordDecl(clang::CXXRecordDecl *Decl); bool TraverseCXXConstructorDecl(clang::CXXConstructorDecl *Decl); bool TraverseCXXMethodDecl(clang::CXXMethodDecl *Decl); @@ -40,6 +41,7 @@ class ASTNormalize : public clang::RecursiveASTVisitor { bool TraverseClassTemplatePartialSpecializationDecl(clang::ClassTemplatePartialSpecializationDecl *Decl); bool VisitNamespaceDecl(clang::NamespaceDecl *Decl); + bool VisitRecordDecl(clang::RecordDecl *Decl); bool VisitCXXRecordDecl(clang::CXXRecordDecl *Decl); bool VisitEnumDecl(clang::EnumDecl *Decl); bool VisitFieldDecl(clang::FieldDecl *Decl); diff --git a/src/alpha/include/header_processor.hpp b/src/alpha/include/header_processor.hpp index 8d3a1d4..c154a82 100644 --- a/src/alpha/include/header_processor.hpp +++ b/src/alpha/include/header_processor.hpp @@ -4,6 +4,7 @@ #include #include +#include "comm_def.hpp" #include "session.hpp" PARSING_STATUS processHeaderPairAlpha(const std::string& projectRoot1, @@ -12,4 +13,5 @@ PARSING_STATUS processHeaderPairAlpha(const std::string& projectRoot1, const std::string& file2, const std::string& reportFormat, const std::vector& IncludePaths, - const std::vector& macroFlags); + const std::vector& macroFlags, + const LANG_OPTIONS lang); diff --git a/src/alpha/include/tree_builder.hpp b/src/alpha/include/tree_builder.hpp index 6eafb4f..1d754f3 100644 --- a/src/alpha/include/tree_builder.hpp +++ b/src/alpha/include/tree_builder.hpp @@ -49,6 +49,7 @@ class TreeBuilder { void normalizeValueDeclNode(const clang::ValueDecl *Decl, unsigned int pos = -1); // Node building methods + bool BuildRecordNode(clang::RecordDecl* Decl); bool BuildCXXRecordNode(clang::CXXRecordDecl* Decl); bool BuildEnumNode(clang::EnumDecl* Decl); bool BuildFunctionNode(clang::FunctionDecl* Decl); diff --git a/src/alpha/src/astnormalizer.cpp b/src/alpha/src/astnormalizer.cpp index 14b0d92..a9a4535 100644 --- a/src/alpha/src/astnormalizer.cpp +++ b/src/alpha/src/astnormalizer.cpp @@ -64,6 +64,26 @@ bool alpha::ASTNormalize::TraverseNamespaceDecl(clang::NamespaceDecl *Decl) { return true; } +bool alpha::ASTNormalize::TraverseRecordDecl(clang::RecordDecl *Decl) { + // Only handle pure C RecordDecl — CXXRecordDecl is a subclass and has its own Traverse + if (llvm::isa(Decl)) { + return RecursiveASTVisitor::TraverseRecordDecl(Decl); + } + + RecursiveASTVisitor::TraverseRecordDecl(Decl); + + llvm::SmallString<128> nameBuf; + llvm::raw_svector_ostream OS(nameBuf); + Decl->printName(OS); + + if (treeBuilder.IsFromMainFileAndNotLocal(Decl) && Decl->isThisDeclarationADefinition() && !nameBuf.empty()) { + treeBuilder.PopName(); + treeBuilder.PopNode(); + } + + return true; +} + bool alpha::ASTNormalize::TraverseCXXRecordDecl(clang::CXXRecordDecl *Decl) { RecursiveASTVisitor::TraverseCXXRecordDecl(Decl); @@ -146,6 +166,12 @@ bool alpha::ASTNormalize::VisitNamespaceDecl(clang::NamespaceDecl *Decl) { return false; } +bool alpha::ASTNormalize::VisitRecordDecl(clang::RecordDecl *Decl) { + // Only handle pure C RecordDecl — skip if this is actually a CXXRecordDecl + if (llvm::isa(Decl)) return true; + return treeBuilder.BuildRecordNode(Decl); +} + bool alpha::ASTNormalize::VisitCXXRecordDecl(clang::CXXRecordDecl *Decl) { return treeBuilder.BuildCXXRecordNode(Decl); } diff --git a/src/alpha/src/header_processor.cpp b/src/alpha/src/header_processor.cpp index db0aa81..f357a44 100644 --- a/src/alpha/src/header_processor.cpp +++ b/src/alpha/src/header_processor.cpp @@ -33,17 +33,27 @@ using namespace clang; using namespace clang::tooling; using namespace llvm; -#ifndef CLANG_FLAGS -#define CLANG_FLAGS "" -#endif - namespace { std::vector getClangFlags(const std::vector& includePaths, - const std::vector& macroFlags) { + const std::vector& macroFlags, + const LANG_OPTIONS lang) { std::vector flags; - std::istringstream iss(CLANG_FLAGS); + const char* rawFlags; + switch (lang) { + case LANG_OPTIONS::C: + rawFlags = CLANG_FLAGS_C; + break; + case LANG_OPTIONS::CPP: + rawFlags = CLANG_FLAGS_CPP; + break; + default: + rawFlags = CLANG_FLAGS_CPP; + break; + } + + std::istringstream iss(rawFlags); std::string flag; while (iss >> flag) { flags.emplace_back(std::move(flag)); @@ -118,7 +128,8 @@ PARSING_STATUS processHeaderPairAlpha(const std::string& project1, const std::string& file2, const std::string& reportFormat, const std::vector& IncludePaths, - const std::vector& macroFlags) { + const std::vector& macroFlags, + const LANG_OPTIONS lang) { if (!DebugConfig::getInstance().initialize()) { armor::user_error() << "Failed to open diagnostics log <" << LOG_FILE_PATH << ">, using stderr\n"; @@ -130,8 +141,8 @@ PARSING_STATUS processHeaderPairAlpha(const std::string& project1, std::vector inclusionPaths1 = resolveInternalIncludePaths(IncludePaths, project1); std::vector inclusionPaths2 = resolveInternalIncludePaths(IncludePaths, project2); - std::vector Flags1 = getClangFlags(inclusionPaths1, macroFlags); - std::vector Flags2 = getClangFlags(inclusionPaths2, macroFlags); + std::vector Flags1 = getClangFlags(inclusionPaths1, macroFlags, lang); + std::vector Flags2 = getClangFlags(inclusionPaths2, macroFlags, lang); Flags1.insert(Flags1.end(), inclusion_paths1.begin(), inclusion_paths1.end()); Flags2.insert(Flags2.end(), inclusion_paths2.begin(), inclusion_paths2.end()); diff --git a/src/alpha/src/preprocessor.cpp b/src/alpha/src/preprocessor.cpp index 1a168f2..ac43833 100644 --- a/src/alpha/src/preprocessor.cpp +++ b/src/alpha/src/preprocessor.cpp @@ -5,6 +5,7 @@ #include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" #include +#include #include #include #include @@ -34,8 +35,10 @@ void ASTNormalizerPreprocessor::InclusionDirective( if (HashLoc.isValid()) { clang::PresumedLoc PLoc = SM->getPresumedLoc(HashLoc); if (PLoc.isValid()) { - armor::debug() << "Failed include - RelativePath: " << RelativePath << " at " << PLoc.getFilename() << "\n"; - context->getSourceRangeTracker().addFatalDirective(RelativePath,PLoc.getFilename()); + if(llvm::StringRef fileName = PLoc.getFilename(); !fileName.empty() && !RelativePath.empty()){ + armor::debug() << "Failed include - RelativePath: " << RelativePath << " at " << PLoc.getFilename() << "\n"; + context->getSourceRangeTracker().addFatalDirective(RelativePath,PLoc.getFilename()); + } } } diff --git a/src/alpha/src/tree_builder.cpp b/src/alpha/src/tree_builder.cpp index 7ecf1af..67bc812 100644 --- a/src/alpha/src/tree_builder.cpp +++ b/src/alpha/src/tree_builder.cpp @@ -156,6 +156,48 @@ void alpha::TreeBuilder::normalizeValueDeclNode(const clang::ValueDecl *Decl, un PopName(); } +bool alpha::TreeBuilder::BuildRecordNode(clang::RecordDecl* Decl) { + + // Building RecordDecl for C specifaically + if (llvm::isa(Decl)) return true; + + llvm::SmallString<128> nameBuf; + llvm::raw_svector_ostream OS(nameBuf); + Decl->printName(OS); + + if (!IsFromMainFileAndNotLocal(Decl) || !Decl->isThisDeclarationADefinition() || nameBuf.empty()) { + return false; + } + + if (Decl->getTypedefNameForAnonDecl()) { + return false; + } else { + if (Decl->hasNameForLinkage()) { + PushName(nameBuf); + } + } + + const std::string qualifiedName = GetCurrentQualifiedName(); + auto recordNode = std::make_shared(); + + recordNode->qualifiedName = qualifiedName; + + armor::debug() << "VisitRecordDecl (C): " << qualifiedName << "\n"; + + if (Decl->isStruct()) { + recordNode->kind = NodeKind::Struct; + recordNode->hash = generateHash(qualifiedName, NodeKind::Struct); + } else if (Decl->isUnion()) { + recordNode->kind = NodeKind::Union; + recordNode->hash = generateHash(qualifiedName, NodeKind::Union); + } + + AddNode(recordNode); + PushNode(recordNode); + + return true; +} + bool alpha::TreeBuilder::BuildCXXRecordNode(clang::CXXRecordDecl* Decl) { llvm::SmallString<128> nameBuf; diff --git a/src/armor/src/options_handler.cpp b/src/armor/src/options_handler.cpp index 8f7f3d4..8196009 100644 --- a/src/armor/src/options_handler.cpp +++ b/src/armor/src/options_handler.cpp @@ -4,20 +4,39 @@ #include #include #include +#include +#include #include "CLI/CLI.hpp" #include "llvm/Support/raw_ostream.h" +#include "comm_def.hpp" #include "options_handler.hpp" #include #include "alpha/include/header_processor.hpp" #include "beta/include/header_processor.hpp" +#include "report_utils.hpp" +#include "diff_utils.hpp" #include "logger.hpp" #ifndef TOOL_VERSION #define TOOL_VERSION "" #endif +LANG_OPTIONS stringToLangOption(const std::string& lang) { + // Convert to lowercase for case-insensitive comparison + std::string lowerLang = lang; + std::transform(lowerLang.begin(), lowerLang.end(), lowerLang.begin(), + [](unsigned char c){ return std::tolower(c); }); + + if (lowerLang == LANG_C) { + return LANG_OPTIONS::C; + } else if (lowerLang == LANG_CPP) { + return LANG_OPTIONS::CPP; + } + return LANG_OPTIONS::CPP; // default to C++ +} + bool filesAreDifferentUsingDiff(const std::string &file1, const std::string &file2) { std::string command = "diff -q " + file1 + " " + file2 + " > /dev/null"; return std::system(command.c_str()) != 0; @@ -30,6 +49,7 @@ bool runArmorTool(int argc, const char **argv) { std::vector headers; std::string headerSubDir; std::string reportFormat = "html"; + std::string language = LANG_CPP; // default to C++ bool dumpAstDiff = false; std::string debugLevel = ""; std::vector IncludePaths; @@ -61,6 +81,9 @@ bool runArmorTool(int argc, const char **argv) { app.add_option("--report-format,-r", reportFormat, "Report format: html (default).\n" "If json is provided, both html and json reports will be generated.") ->check(CLI::IsMember({"html", "json"})); + app.add_option("--lang,-l", language, "Language mode: cpp (default) or c.\n" + "Use 'c' for C headers, 'cpp' for C++ headers.") + ->transform(CLI::IsMember({LANG_C, LANG_CPP}, CLI::ignore_case)); app.add_flag("--dump-ast-diff", dumpAstDiff, "Dump AST diff JSON files for debugging"); app.set_version_flag("--version,-v", TOOL_VERSION); app.add_option("--log-level", debugLevel, "Set debug log level: ERROR, LOG, INFO (default), DEBUG") @@ -102,6 +125,10 @@ bool runArmorTool(int argc, const char **argv) { debugConfig.setLevel(DebugConfig::Level::NONE); } + // Convert language string to LANG_OPTIONS enum + LANG_OPTIONS langOption = stringToLangOption(language); + armor::info() << "Language mode set to: " << language << "\n"; + bool processed = false; std::vector headersToCompare; if (!headers.empty()) { @@ -110,31 +137,87 @@ bool runArmorTool(int argc, const char **argv) { if (!headerSubDir.empty()) { file1 = projectRoot1 + "/" + headerSubDir + "/" + header; file2 = projectRoot2 + "/" + headerSubDir + "/" + header; - } else { + } + else { file1 = projectRoot1 + "/" + header; file2 = projectRoot2 + "/" + header; } armor::user_print() << "Processing files: " << file1 << " " << file2 << "\n"; - if (!std::filesystem::exists(file1)) { + if ( !std::filesystem::exists(file1) && !std::filesystem::exists(file2) ){ + armor::user_error() << "Missing old and new versions of header : \n" << file1 << "\n" << file2 << "\n"; + std::filesystem::create_directories("armor_reports/html_reports"); + std::filesystem::create_directories("armor_reports/json_reports"); + } + else if (!std::filesystem::exists(file1)) { armor::user_error() << "Missing header in older version: " << file1 << "\n"; - } else if (!std::filesystem::exists(file2)) { + std::string headerName = std::filesystem::path(file2).filename().string(); + const auto& [jsonReportFile, htmlReportFile] = prepare_report_output_dirs(headerName); + generate_json_report( + {}, + jsonReportFile, + static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), + static_cast(UnParsedDiffStatus::UN_CHANGED), + "backward_compatible", + "BACKWARD_COMPATIBLE", + "Missing header in older version" + ); + generate_html_report( + {}, + htmlReportFile, + NO_PARSER, + static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), + static_cast(UnParsedDiffStatus::UN_CHANGED), + "backward_compatible", + "BACKWARD_COMPATIBLE", + "Missing header in older version", + {false, true} + ); + } + else if (!std::filesystem::exists(file2)) { armor::user_error() << "Missing header in newer version: " << file2 << "\n"; - } else if (filesAreDifferentUsingDiff(file1, file2)) { - PARSING_STATUS parsingStatus = processHeaderPairAlpha(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); - switch (parsingStatus) { - case NO_FATAL_ERRORS: - armor::info() << "Processing Headers again via beta parser\n"; - processHeaderPairBeta(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); - break; - case FATAL_ERRORS: - armor::info() << "Processing Headers stopped at alpha parser\n"; - break; + std::string headerName = std::filesystem::path(file1).filename().string(); + const auto& [jsonReportFile, htmlReportFile] = prepare_report_output_dirs(headerName); + generate_json_report( + {}, + jsonReportFile, + static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), + static_cast(UnParsedDiffStatus::UN_CHANGED), + "backward_incompatible", + "BACKWARD_INCOMPATIBLE", + "Missing header in newer version" + ); + generate_html_report( + {}, + htmlReportFile, + NO_PARSER, + static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), + static_cast(UnParsedDiffStatus::UN_CHANGED), + "backward_incompatible", + "BACKWARD_INCOMPATIBLE", + "Missing header in newer version", + {true, false} + ); + } + else{ + if (filesAreDifferentUsingDiff(file1, file2)) { + PARSING_STATUS parsingStatus = processHeaderPairAlpha(projectRoot1, file1, projectRoot2, file2, reportFormat, + IncludePaths, macros, langOption); + switch (parsingStatus) { + case NO_FATAL_ERRORS: + armor::info() << "Processing Headers again via beta parser\n"; + processHeaderPairBeta(projectRoot1, file1, projectRoot2, file2, reportFormat, + IncludePaths, macros, langOption); + break; + case FATAL_ERRORS: + armor::info() << "Processing Headers stopped at alpha parser\n"; + break; + } + processed = true; + } + else { + armor::user_print() << "No differences found between: " << file1 << " and " << file2 << "\n"; + return true; } - processed = true; - } else { - armor::user_print() << "No differences found between: " << file1 << " and " << file2 << "\n"; } } } @@ -154,27 +237,79 @@ bool runArmorTool(int argc, const char **argv) { std::string file1 = dir1 + "/" + header; std::string file2 = dir2 + "/" + header; armor::user_print() << "Processing files: " << file1 << " " << file2 << "\n"; - if (!std::filesystem::exists(file1)) { + if ( !std::filesystem::exists(file1) && !std::filesystem::exists(file2) ){ + armor::user_error() << "Missing old and new versions of header : \n" << file1 << "\n" << file2 << "\n"; + } + else if (!std::filesystem::exists(file1)) { armor::user_error() << "Missing header in older version: " << file1 << "\n"; + std::string headerName = std::filesystem::path(file2).filename().string(); + const auto& [jsonReportFile, htmlReportFile] = prepare_report_output_dirs(headerName); + generate_json_report( + {}, + jsonReportFile, + static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), + static_cast(UnParsedDiffStatus::UN_CHANGED), + "backward_compatible", + "BACKWARD_COMPATIBLE", + "Missing header in older version" + ); + generate_html_report( + {}, + htmlReportFile, + NO_PARSER, + static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), + static_cast(UnParsedDiffStatus::UN_CHANGED), + "backward_compatible", + "BACKWARD_COMPATIBLE", + "Missing header in older version", + {false, true} + ); } else if (!std::filesystem::exists(file2)) { armor::user_error() << "Missing header in newer version: " << file2 << "\n"; - } else if (filesAreDifferentUsingDiff(file1, file2)) { - PARSING_STATUS parsingStatus = processHeaderPairAlpha(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); - switch (parsingStatus) { - case NO_FATAL_ERRORS: - armor::info() << "Processing Headers again via v2\n"; - processHeaderPairBeta(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); - break; - case FATAL_ERRORS: - armor::info() << "Processing Headers stopped at v1\n"; - break; + std::string headerName = std::filesystem::path(file1).filename().string(); + const auto& [jsonReportFile, htmlReportFile] = prepare_report_output_dirs(headerName); + generate_json_report( + {}, + jsonReportFile, + static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), + static_cast(UnParsedDiffStatus::UN_CHANGED), + "backward_incompatible", + "BACKWARD_INCOMPATIBLE", + "Missing header in newer version" + ); + generate_html_report( + {}, + htmlReportFile, + NO_PARSER, + static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), + static_cast(UnParsedDiffStatus::UN_CHANGED), + "backward_incompatible", + "BACKWARD_INCOMPATIBLE", + "Missing header in newer version", + {true, false} + ); + } + else{ + if (filesAreDifferentUsingDiff(file1, file2)) { + PARSING_STATUS parsingStatus = processHeaderPairAlpha(projectRoot1, file1, projectRoot2, file2, reportFormat, + IncludePaths, macros, langOption); + switch (parsingStatus) { + case NO_FATAL_ERRORS: + armor::info() << "Processing Headers again via v2\n"; + processHeaderPairBeta(projectRoot1, file1, projectRoot2, file2, reportFormat, + IncludePaths, macros, langOption); + break; + case FATAL_ERRORS: + armor::info() << "Processing Headers stopped at v1\n"; + break; + } + processed = true; + } + else { + llvm::outs()<<"No diff beta\n"; + armor::user_print() << "No differences found between: " << file1 << " and " << file2 << "\n"; + return true; } - processed = true; - } else { - llvm::outs()<<"No diff beta\n"; - armor::user_print() << "No differences found between: " << file1 << " and " << file2 << "\n"; } } } diff --git a/src/beta/include/astnormalizer.hpp b/src/beta/include/astnormalizer.hpp index 1e17d99..3723823 100644 --- a/src/beta/include/astnormalizer.hpp +++ b/src/beta/include/astnormalizer.hpp @@ -30,6 +30,7 @@ class ASTNormalize : public clang::RecursiveASTVisitor { ASTNormalize(beta::APISession* session, beta::ASTNormalizedContext* context, clang::ASTContext* clangContext); bool TraverseNamespaceDecl(clang::NamespaceDecl *Decl); + bool TraverseRecordDecl(clang::RecordDecl *Decl); bool TraverseCXXRecordDecl(clang::CXXRecordDecl *Decl); bool TraverseCXXConstructorDecl(clang::CXXConstructorDecl *Decl); bool TraverseCXXMethodDecl(clang::CXXMethodDecl* Decl); @@ -57,6 +58,7 @@ class ASTNormalize : public clang::RecursiveASTVisitor { bool TraverseTemplateTemplateParmDecl(clang::TemplateTemplateParmDecl *Decl); bool VisitNamespaceDecl(clang::NamespaceDecl *Decl); + bool VisitRecordDecl(clang::RecordDecl *Decl); bool VisitCXXRecordDecl(clang::CXXRecordDecl *Decl); bool VisitEnumDecl(clang::EnumDecl *Decl); bool VisitFieldDecl(clang::FieldDecl *Decl); diff --git a/src/beta/include/header_processor.hpp b/src/beta/include/header_processor.hpp index b869b7d..67b2f0b 100644 --- a/src/beta/include/header_processor.hpp +++ b/src/beta/include/header_processor.hpp @@ -12,4 +12,5 @@ PARSING_STATUS processHeaderPairBeta(const std::string& projectRoot1, const std::string& file2, const std::string& reportFormat, const std::vector& IncludePaths, - const std::vector& macroFlags); + const std::vector& macroFlags, + const LANG_OPTIONS lang); diff --git a/src/beta/include/tree_builder.hpp b/src/beta/include/tree_builder.hpp index 12b696a..856767d 100644 --- a/src/beta/include/tree_builder.hpp +++ b/src/beta/include/tree_builder.hpp @@ -67,6 +67,7 @@ class TreeBuilder { // Node building methods (supported) bool BuildCXXRecordNode(clang::CXXRecordDecl* Decl); + bool BuildRecordNode(clang::RecordDecl* Decl); bool BuildEnumNode(clang::EnumDecl* Decl); bool BuildFunctionNode(clang::FunctionDecl* Decl); bool BuildTypedefDecl(clang::TypedefDecl *Decl); diff --git a/src/beta/src/astnormalizer.cpp b/src/beta/src/astnormalizer.cpp index 7a70bd9..4ae3198 100644 --- a/src/beta/src/astnormalizer.cpp +++ b/src/beta/src/astnormalizer.cpp @@ -11,6 +11,7 @@ #include "tree_builder.hpp" #include "comment_handler.hpp" #include "preprocesor.hpp" +#include "clang/AST/APValue.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/RecursiveASTVisitor.h" @@ -106,6 +107,18 @@ bool beta::ASTNormalize::TraverseNamespaceDecl(clang::NamespaceDecl *Decl) { return true; } +bool beta::ASTNormalize::TraverseRecordDecl(clang::RecordDecl *Decl) { + + RecursiveASTVisitor::TraverseRecordDecl(Decl); + + if (!llvm::isa(Decl) && treeBuilder.IsDeclFromMainFileAndNotLocal(Decl)) { + treeBuilder.PopName(); + treeBuilder.PopNode(); + } + + return true; +} + bool beta::ASTNormalize::TraverseCXXRecordDecl(clang::CXXRecordDecl *Decl) { RecursiveASTVisitor::TraverseCXXRecordDecl(Decl); @@ -248,6 +261,10 @@ bool beta::ASTNormalize::VisitNamespaceDecl(clang::NamespaceDecl *Decl) { return true; } +bool beta::ASTNormalize::VisitRecordDecl(clang::RecordDecl *Decl) { + return treeBuilder.BuildRecordNode(Decl); +} + bool beta::ASTNormalize::VisitCXXRecordDecl(clang::CXXRecordDecl *Decl) { return treeBuilder.BuildCXXRecordNode(Decl); } diff --git a/src/beta/src/diffengine.cpp b/src/beta/src/diffengine.cpp index eaa6460..3436823 100644 --- a/src/beta/src/diffengine.cpp +++ b/src/beta/src/diffengine.cpp @@ -392,7 +392,7 @@ json diffTrees( hasHashMapDifference(inactiveUnhandledDeclsHashMap2, inactiveUnhandledDeclsHashMap1); ParsedDiffStatus parsedStatus = determineStatus(hasASTDiff, hasCommentsDiff, hasUnhandledDeclsDiff); - UnParsedDiffStatus unparsedStatus = hasInactiveUnhandledDeclsDiff ? UnParsedDiffStatus::CHANGED : UnParsedDiffStatus::UN_CHANGES; + UnParsedDiffStatus unparsedStatus = hasInactiveUnhandledDeclsDiff ? UnParsedDiffStatus::CHANGED : UnParsedDiffStatus::UN_CHANGED; json result; result[PARSED_STATUS] = parsedStatus; diff --git a/src/beta/src/header_processor.cpp b/src/beta/src/header_processor.cpp index e749e01..bd94032 100644 --- a/src/beta/src/header_processor.cpp +++ b/src/beta/src/header_processor.cpp @@ -32,17 +32,27 @@ using namespace clang; using namespace clang::tooling; using namespace llvm; -#ifndef CLANG_FLAGS -#define CLANG_FLAGS "" -#endif - namespace { std::vector getClangFlags(const std::vector& includePaths, - const std::vector& macroFlags) { + const std::vector& macroFlags, + const LANG_OPTIONS lang) { std::vector flags; - std::istringstream iss(CLANG_FLAGS); + const char* rawFlags; + switch (lang) { + case LANG_OPTIONS::C: + rawFlags = CLANG_FLAGS_C; + break; + case LANG_OPTIONS::CPP: + rawFlags = CLANG_FLAGS_CPP; + break; + default: + rawFlags = CLANG_FLAGS_CPP; + break; + } + + std::istringstream iss(rawFlags); std::string flag; while (iss >> flag) { flags.emplace_back(std::move(flag)); @@ -116,7 +126,8 @@ PARSING_STATUS processHeaderPairBeta(const std::string& project1, const std::string& file2, const std::string& reportFormat, const std::vector& IncludePaths, - const std::vector& macroFlags) { + const std::vector& macroFlags, + const LANG_OPTIONS lang) { if (!DebugConfig::getInstance().initialize()) { armor::user_error() << "Failed to open diagnostics log <" << LOG_FILE_PATH << ">, using stderr\n"; @@ -128,8 +139,8 @@ PARSING_STATUS processHeaderPairBeta(const std::string& project1, std::vector inclusionPaths1 = resolveInternalIncludePaths(IncludePaths, project1); std::vector inclusionPaths2 = resolveInternalIncludePaths(IncludePaths, project2); - std::vector Flags1 = getClangFlags(inclusionPaths1, macroFlags); - std::vector Flags2 = getClangFlags(inclusionPaths2, macroFlags); + std::vector Flags1 = getClangFlags(inclusionPaths1, macroFlags, lang); + std::vector Flags2 = getClangFlags(inclusionPaths2, macroFlags, lang); Flags1.insert(Flags1.end(), inclusion_paths1.begin(), inclusion_paths1.end()); Flags2.insert(Flags2.end(), inclusion_paths2.begin(), inclusion_paths2.end()); diff --git a/src/beta/src/preprocessor.cpp b/src/beta/src/preprocessor.cpp index f231aa2..63306aa 100644 --- a/src/beta/src/preprocessor.cpp +++ b/src/beta/src/preprocessor.cpp @@ -107,6 +107,7 @@ uint64_t ASTNormalizerPreprocessor::generateHashFromOffsets(unsigned startOffset else{ semanticHash = FibonacciHash::hash(sourceText); } + if(!isActive) TEST_LOG << "(IN-ACTIVE)\n"; TEST_LOG << semanticHash << "\n"; TEST_LOG << sourceText << "\n----------------------------------------\n"; diff --git a/src/beta/src/tree_builder.cpp b/src/beta/src/tree_builder.cpp index fb7a6ef..fc6adb9 100644 --- a/src/beta/src/tree_builder.cpp +++ b/src/beta/src/tree_builder.cpp @@ -340,6 +340,76 @@ void beta::TreeBuilder::normalizeValueDeclNode(const clang::ValueDecl *Decl, uns PopName(); } +bool beta::TreeBuilder::BuildRecordNode(clang::RecordDecl* Decl) { + + // Building RecordDecl for C specifaically + if (!IsDeclFromMainFileAndNotLocal(Decl)) return false; + + if(llvm::isa(Decl)) return true; + + llvm::SmallString<128> nameBuf; + llvm::raw_svector_ostream OS(nameBuf); + Decl->printName(OS); + + if (!nameBuf.empty()) { + PushName(nameBuf); + } + else { + if (const auto *typedefForAnon = Decl->getTypedefNameForAnonDecl()) { + typedefForAnon->printName(OS); + PushName(nameBuf); + } + else { + if (Decl->isEmbeddedInDeclarator() && !Decl->isFreeStanding()) { + if (const clang::ValueDecl* ValueDecl = + llvm::dyn_cast_or_null(Decl->getNextDeclInContext())) { + const clang::QualType QT = armor::unwrapTypeModifiers(ValueDecl->getType()); + if (QT->getAsTagDecl()->Equals(Decl)) { + ValueDecl->printName(OS); + PushName(nameBuf); + } + } + else if (const clang::TypedefDecl* TypeDefDecl = + llvm::dyn_cast_or_null(Decl->getNextDeclInContext())) { + const clang::QualType QT = armor::unwrapTypeModifiers(TypeDefDecl->getUnderlyingType()); + if (QT->getAsTagDecl()->Equals(Decl)) { + TypeDefDecl->printName(OS); + PushName(nameBuf); + } + } + } + else { + if (Decl->isStruct()) PushName("(Anon::Struct)"); + if (Decl->isUnion()) PushName("(Anon::Union)"); + } + } + } + + const std::string USR = generateUSRForDecl(Decl); + const std::string NSR = generateNSRForDecl(Decl); + + const auto it = context->usrNodeMap.find(USR); + std::shared_ptr recordNode = + (it != context->usrNodeMap.end()) ? it->second : std::make_shared(); + recordNode->NSR = NSR; + recordNode->USR = USR; + if (it == context->usrNodeMap.end()) AddNode(recordNode); + recordNode->qualifiedName = GetCurrentQualifiedName(); + context->usrNodeMap.insert_or_assign(std::move(USR), recordNode); + + armor::debug() << "VisitRecordDecl (C): " << recordNode->qualifiedName << "\n"; + + if (Decl->isStruct()) { + recordNode->kind = NodeKind::Struct; + } + else if (Decl->isUnion()) { + recordNode->kind = NodeKind::Union; + } + + PushNode(recordNode); + return true; +} + bool beta::TreeBuilder::BuildCXXRecordNode(clang::CXXRecordDecl* Decl) { if (!IsDeclFromMainFileAndNotLocal(Decl)) return false; diff --git a/src/common/include/comm_def.hpp b/src/common/include/comm_def.hpp index 160131c..b82977f 100644 --- a/src/common/include/comm_def.hpp +++ b/src/common/include/comm_def.hpp @@ -57,9 +57,19 @@ enum PARSING_STATUS { enum PARSER{ ALPHA_PARSER = 0, - BETA_PARSER = 1 + BETA_PARSER = 1, + NO_PARSER = 2 }; +enum LANG_OPTIONS{ + C = 0, + CPP = 1 +}; + +// Language option string constants +const std::string LANG_C = "c"; +const std::string LANG_CPP = "cpp"; + const std::string LOG_FILE_PATH = "debug_output/logs/diagnostics.log"; const std::string TEST_LOG_FILE_PATH = "output.txt"; \ No newline at end of file diff --git a/src/common/include/diff_utils.hpp b/src/common/include/diff_utils.hpp index 4c8879f..243484a 100644 --- a/src/common/include/diff_utils.hpp +++ b/src/common/include/diff_utils.hpp @@ -39,7 +39,7 @@ enum class ParsedDiffStatus { }; enum class UnParsedDiffStatus{ - UN_CHANGES =0, + UN_CHANGED =0, CHANGED = 1 }; diff --git a/src/common/include/html_template.hpp b/src/common/include/html_template.hpp index 777d1f8..5bccb21 100644 --- a/src/common/include/html_template.hpp +++ b/src/common/include/html_template.hpp @@ -35,4 +35,18 @@ tr:hover { background-color: #ddd; } Header NameAPI NameDescriptionChange TypeSource Compatibility )"; +const std::string SIMPLE_HEADER = R"( +

API Compatibility Report

+

ARMOR Report

+ +)"; + const std::string HTML_FOOTER = "
"; diff --git a/src/common/include/report_utils.hpp b/src/common/include/report_utils.hpp index a4b4177..9b0e1ae 100644 --- a/src/common/include/report_utils.hpp +++ b/src/common/include/report_utils.hpp @@ -39,9 +39,24 @@ void generate_html_report(const std::vector& processed_data, int unparsed_status, const std::string& agg_compatibility, const char* overall_status, - const char* reason + const char* reason, + std::pair files_exists = {true, true} ); +/** + * @brief Create the standard output directories for reports and AST debug dumps, + * and return the path to the HTML report file for the given header. + * + * Creates: + * - debug_output/ast_diffs + * - armor_reports/html_reports + * + * @param headerName Basename of the header file (e.g. "foo.h"). + * @return std::string Full path to the HTML report file + * ("armor_reports/html_reports/api_diff_report_.html"). + */ +std::pair prepare_report_output_dirs(const std::string& headerName); + /** * @brief Generate a JSON report from processed API changes. * diff --git a/src/common/src/categorization.cpp b/src/common/src/categorization.cpp index be72495..db358bd 100644 --- a/src/common/src/categorization.cpp +++ b/src/common/src/categorization.cpp @@ -50,7 +50,7 @@ const char* getOverAllCategory(unsigned int parsedDiffStatus, unsigned int unPar break; } break; - case UnParsedDiffStatus::UN_CHANGES: + case UnParsedDiffStatus::UN_CHANGED: switch (parsedStatus) { case ParsedDiffStatus::FATAL_ERRORS: overAllStatus = OverAllStatus::FATAL_ERRORS; @@ -103,7 +103,7 @@ const char* getReasonForCategorization(unsigned int parsedDiffStatus, unsigned i return ERROR_INVALID_PARSED_STATUS; } break; - case UnParsedDiffStatus::UN_CHANGES: + case UnParsedDiffStatus::UN_CHANGED: switch (parsedStatus) { case ParsedDiffStatus::FATAL_ERRORS: return CRITICAL_PARSING_ERRORS; diff --git a/src/common/src/diff_utils.cpp b/src/common/src/diff_utils.cpp index c044eb4..5022f4c 100644 --- a/src/common/src/diff_utils.cpp +++ b/src/common/src/diff_utils.cpp @@ -92,7 +92,7 @@ const std::string serialize(const ParsedDiffStatus& diff_status){ } const std::string serialize(const UnParsedDiffStatus& diff_status){ switch (diff_status) { - case UnParsedDiffStatus::UN_CHANGES: return "UNCHANGED"; + case UnParsedDiffStatus::UN_CHANGED: return "UNCHANGED"; case UnParsedDiffStatus::CHANGED: return "CHANGED"; default: return "FATAL_ERRORS"; } diff --git a/src/common/src/report_utils.cpp b/src/common/src/report_utils.cpp index f9bb7df..bf73343 100644 --- a/src/common/src/report_utils.cpp +++ b/src/common/src/report_utils.cpp @@ -1,14 +1,19 @@ // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -// SPDX-License-Identifier: BSD-3-Clause#include +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include #include #include #include +#include #include #include #include #include #include #include +#include #include "comm_def.hpp" #include "report_utils.hpp" @@ -1029,6 +1034,15 @@ static std::vector group_records_by_function(const std::vector& rows // Public API // ----------------------------------------------------------------------------- +std::pair prepare_report_output_dirs(const std::string& headerName) +{ + std::filesystem::create_directories("armor_reports/html_reports"); + std::filesystem::create_directories("armor_reports/json_reports"); + std::string json_out = "armor_reports/json_reports/api_diff_report_" + headerName + ".json"; + std::string html_out = "armor_reports/html_reports/api_diff_report_" + headerName + ".html"; + return {json_out, html_out}; +} + std::vector preprocess_api_changes(const json& api_differences, const std::string& header_file_path) { @@ -1136,21 +1150,37 @@ void generate_html_report(const std::vector& processed_data, int unparsed_status, const std::string& agg_compatibility, const char* overall_status, - const char* reason + const char* reason, + std::pair files_exists ) { std::ofstream html(output_html_path); ParsedDiffStatus parsedStatus = static_cast(parsed_status); UnParsedDiffStatus unParsedStatus = static_cast(unparsed_status); if (processed_data.empty()) { - html << "

ARMOR Report

\n"; - html << "\n"; - html << " \n"; - html << " \n"; - html << " \n"; - html << "
\n"; - html << " Skipping ARMOR report generation. Reason: " << reason << "
\n"; - html << "
\n"; + + const auto& [file1_exists, file2_exists] = files_exists; + + if( file1_exists & file2_exists ){ + html << "

ARMOR Report

\n"; + html << "\n"; + html << " \n"; + html << " \n"; + html << " \n"; + html << "
\n"; + html << " Skipping ARMOR report generation. Reason: " << reason << "
\n"; + html << "
\n"; + } + else{ + assert( file1_exists | file2_exists ); + html << SIMPLE_HEADER; + html << "
\n"; + html << "Overall status: " << overall_status << "
\n"; + html << "Reason: " << reason << "
\n"; + html << "
\n"; + + } } else { @@ -1161,6 +1191,8 @@ void generate_html_report(const std::vector& processed_data, case BETA_PARSER: html << BETA_HTML_HEADER; break; + default: + break; } auto grouped = group_records_by_function(processed_data); diff --git a/src/tests/__init__.py b/src/tests/__init__.py new file mode 100644 index 0000000..6ffd676 --- /dev/null +++ b/src/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +# Make tests directory a Python package \ No newline at end of file diff --git a/src/tests/alpha/__init__.py b/src/tests/alpha/__init__.py new file mode 100644 index 0000000..9924184 --- /dev/null +++ b/src/tests/alpha/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +# Make alpha directory a Python package \ No newline at end of file diff --git a/src/tests/alpha/functional/__init__.py b/src/tests/alpha/functional/__init__.py new file mode 100644 index 0000000..d920b29 --- /dev/null +++ b/src/tests/alpha/functional/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +# Make functional directory a Python package \ No newline at end of file diff --git a/src/tests/alpha/functional/anonymous_type_changes/__init__.py b/src/tests/alpha/functional/anonymous_type_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/anonymous_type_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/anonymous_type_changes/test_alpha_anonymous_type_changes.py b/src/tests/alpha/functional/anonymous_type_changes/test_alpha_anonymous_type_changes.py index 068d846..eaae678 100644 --- a/src/tests/alpha/functional/anonymous_type_changes/test_alpha_anonymous_type_changes.py +++ b/src/tests/alpha/functional/anonymous_type_changes/test_alpha_anonymous_type_changes.py @@ -6,7 +6,7 @@ from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -27,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} diff --git a/src/tests/alpha/functional/conftest.py b/src/tests/alpha/functional/conftest.py index fe39995..01c4235 100644 --- a/src/tests/alpha/functional/conftest.py +++ b/src/tests/alpha/functional/conftest.py @@ -38,3 +38,10 @@ def dependent_binary_args(request): prj_root2 = os.path.join(curr_dir, "v2") return [prj_root1, prj_root2, "mylib.h","-Iinclude", "--dump-ast-diff", "-r", "json"] +@pytest.fixture +def binary_args_c(request): + """Returns the arguments for the binary with debug and JSON output enabled.""" + curr_dir = os.path.dirname(request.fspath) + prj_root1 = os.path.join(curr_dir, "v1") + prj_root2 = os.path.join(curr_dir, "v2") + return [prj_root1, prj_root2, "mylib.h", "--lang", "c", "--dump-ast-diff", "-r", "json"] diff --git a/src/tests/alpha/functional/enum_changes/__init__.py b/src/tests/alpha/functional/enum_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/enum_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/enum_changes/test_alpha_enum_changes.py b/src/tests/alpha/functional/enum_changes/test_alpha_enum_changes.py index 068d846..a68ad16 100644 --- a/src/tests/alpha/functional/enum_changes/test_alpha_enum_changes.py +++ b/src/tests/alpha/functional/enum_changes/test_alpha_enum_changes.py @@ -6,7 +6,7 @@ from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -27,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/alpha/functional/function_changes/__init__.py b/src/tests/alpha/functional/function_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/function_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/function_changes/test_alpha_function_changes.py b/src/tests/alpha/functional/function_changes/test_alpha_function_changes.py index 7742917..a68ad16 100644 --- a/src/tests/alpha/functional/function_changes/test_alpha_function_changes.py +++ b/src/tests/alpha/functional/function_changes/test_alpha_function_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/alpha/functional/function_pointer_changes/__init__.py b/src/tests/alpha/functional/function_pointer_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/function_pointer_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/global_variable_changes/__init__.py b/src/tests/alpha/functional/global_variable_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/global_variable_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/global_variable_changes/test_alpha_global_variable_changes.py b/src/tests/alpha/functional/global_variable_changes/test_alpha_global_variable_changes.py index 7742917..a68ad16 100644 --- a/src/tests/alpha/functional/global_variable_changes/test_alpha_global_variable_changes.py +++ b/src/tests/alpha/functional/global_variable_changes/test_alpha_global_variable_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/alpha/functional/global_variable_changes/v2/mylib.h b/src/tests/alpha/functional/global_variable_changes/v2/mylib.h index 32e21ac..d6aa231 100644 --- a/src/tests/alpha/functional/global_variable_changes/v2/mylib.h +++ b/src/tests/alpha/functional/global_variable_changes/v2/mylib.h @@ -1,5 +1,6 @@ -// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause + // Change the type to unsigned int unsigned int v1; diff --git a/src/tests/alpha/functional/sanity/__init__.py b/src/tests/alpha/functional/sanity/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/sanity/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/struct_changes/__init__.py b/src/tests/alpha/functional/struct_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/struct_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/struct_changes/test_alpha_struct_changes.py b/src/tests/alpha/functional/struct_changes/test_alpha_struct_changes.py index 7742917..a68ad16 100644 --- a/src/tests/alpha/functional/struct_changes/test_alpha_struct_changes.py +++ b/src/tests/alpha/functional/struct_changes/test_alpha_struct_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/alpha/functional/template_changes/__init__.py b/src/tests/alpha/functional/template_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/template_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/type_def_changes/__init__.py b/src/tests/alpha/functional/type_def_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/type_def_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/type_def_changes/test_alpha_type_def_changes.py b/src/tests/alpha/functional/type_def_changes/test_alpha_type_def_changes.py index 7742917..a68ad16 100644 --- a/src/tests/alpha/functional/type_def_changes/test_alpha_type_def_changes.py +++ b/src/tests/alpha/functional/type_def_changes/test_alpha_type_def_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/alpha/functional/union_changes/__init__.py b/src/tests/alpha/functional/union_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/alpha/functional/union_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/alpha/functional/union_changes/test_alpha_union_changes.py b/src/tests/alpha/functional/union_changes/test_alpha_union_changes.py index 7742917..a68ad16 100644 --- a/src/tests/alpha/functional/union_changes/test_alpha_union_changes.py +++ b/src/tests/alpha/functional/union_changes/test_alpha_union_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/alpha/src/options_handler.cpp b/src/tests/alpha/src/options_handler.cpp index 225fa61..99a7fe8 100644 --- a/src/tests/alpha/src/options_handler.cpp +++ b/src/tests/alpha/src/options_handler.cpp @@ -1,6 +1,5 @@ // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. // SPDX-License-Identifier: BSD-3-Clause - #include #include #include @@ -16,6 +15,20 @@ #define TOOL_VERSION "" #endif +LANG_OPTIONS stringToLangOption(const std::string& lang) { + // Convert to lowercase for case-insensitive comparison + std::string lowerLang = lang; + std::transform(lowerLang.begin(), lowerLang.end(), lowerLang.begin(), + [](unsigned char c){ return std::tolower(c); }); + + if (lowerLang == LANG_C) { + return LANG_OPTIONS::C; + } else if (lowerLang == LANG_CPP) { + return LANG_OPTIONS::CPP; + } + return LANG_OPTIONS::CPP; // default to C++ +} + bool filesAreDifferentUsingDiff(const std::string &file1, const std::string &file2) { std::string command = "diff -q " + file1 + " " + file2 + " > /dev/null"; return std::system(command.c_str()) != 0; @@ -28,6 +41,7 @@ bool runArmorTool(int argc, const char **argv) { std::vector headers; std::string headerSubDir; std::string reportFormat = "html"; + std::string language = LANG_CPP; // default to C++ bool dumpAstDiff = false; std::string debugLevel = ""; std::vector IncludePaths; @@ -59,6 +73,9 @@ bool runArmorTool(int argc, const char **argv) { app.add_option("--report-format,-r", reportFormat, "Report format: html (default).\n" "If json is provided, both html and json reports will be generated.") ->check(CLI::IsMember({"html", "json"})); + app.add_option("--lang,-l", language, "Language mode: cpp (default) or c.\n" + "Use 'c' for C headers, 'cpp' for C++ headers.") + ->transform(CLI::IsMember({LANG_C, LANG_CPP}, CLI::ignore_case)); app.add_flag("--dump-ast-diff", dumpAstDiff, "Dump AST diff JSON files for debugging"); app.set_version_flag("--version,-v", TOOL_VERSION); app.add_option("--log-level", debugLevel, "Set debug log level: ERROR, LOG, INFO (default), DEBUG") @@ -97,6 +114,10 @@ bool runArmorTool(int argc, const char **argv) { debugConfig.setLevel(DebugConfig::Level::NONE); } + // Convert language string to LANG_OPTIONS enum + LANG_OPTIONS langOption = stringToLangOption(language); + armor::info() << "Language mode set to: " << language << "\n"; + bool processed = false; std::vector headersToCompare; if (!headers.empty()) { @@ -116,10 +137,12 @@ bool runArmorTool(int argc, const char **argv) { armor::user_error() << "Missing header in newer version: " << file2; } else if (filesAreDifferentUsingDiff(file1, file2)) { processHeaderPairAlpha(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); + IncludePaths, macros, langOption); processed = true; - } else { + } + else { armor::user_print() << "No differences found between: " << file1 << " and " << file2; + return true; } } } @@ -141,14 +164,18 @@ bool runArmorTool(int argc, const char **argv) { armor::user_print() << "Processing files: " << file1 << " " << file2; if (!std::filesystem::exists(file1)) { armor::user_error() << "Missing header in older version: " << file1; - } else if (!std::filesystem::exists(file2)) { + } + else if (!std::filesystem::exists(file2)) { armor::user_error() << "Missing header in newer version: " << file2; - } else if (filesAreDifferentUsingDiff(file1, file2)) { + } + else if (filesAreDifferentUsingDiff(file1, file2)) { processHeaderPairAlpha(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); + IncludePaths, macros, langOption); processed = true; - } else { + } + else { armor::user_print() << "No differences found between: " << file1 << " and " << file2; + return true; } } } diff --git a/src/tests/armor/functional/cmd_macros_categorisation/expected_output.txt b/src/tests/armor/functional/cmd_macros_categorisation/expected_output.txt index 8e845bb..51b0bb7 100644 --- a/src/tests/armor/functional/cmd_macros_categorisation/expected_output.txt +++ b/src/tests/armor/functional/cmd_macros_categorisation/expected_output.txt @@ -96,6 +96,7 @@ legacy_configs 16188692334800662690 #if defined(VERSION_2) || defined(VERSION_3) ---------------------------------------- +(IN-ACTIVE) 4317565735544191414 #ifdef ENABLE_LOGGING LogLevel default_log_level; @@ -107,6 +108,7 @@ legacy_configs 7316820520996380391 #ifdef ENABLE_DATA_PROCESSING ---------------------------------------- +(IN-ACTIVE) 8858807332175181956 #ifdef DEBUG_MODE extended_int_t access_count; @@ -116,6 +118,7 @@ legacy_configs 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 4028265945927569883 #ifdef ENABLE_MATH_UTILS struct Calculator { @@ -134,6 +137,7 @@ INLINE extended_float_t calculate(extended_int_t a, extended_int_t b) { } #endif ---------------------------------------- +(IN-ACTIVE) 15971896135806486621 #ifdef ENABLE_NETWORKING struct Connection { @@ -178,6 +182,7 @@ void initialize_connections() { 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 3283656231603163010 #ifdef OLD_API_VERSION struct OldHandle { @@ -197,6 +202,7 @@ struct OldApiData old_api_instances[3] = { }; #endif ---------------------------------------- +(IN-ACTIVE) 13171204464568943967 #ifdef EXPERIMENTAL_FEATURES struct ExperimentalData { @@ -214,6 +220,7 @@ struct ExperimentalConfig experimental_config = { }; #endif ---------------------------------------- +(IN-ACTIVE) 12988119784036996851 #ifdef BETA_FUNCTIONS struct BetaConfig { @@ -232,6 +239,7 @@ struct BetaData beta_data_instance = { }; #endif ---------------------------------------- +(IN-ACTIVE) 6896127565158225636 #ifdef DEPRECATED_SUPPORT struct DeprecatedBuffer { @@ -248,6 +256,7 @@ struct DeprecatedStruct deprecated_instance = { }; #endif ---------------------------------------- +(IN-ACTIVE) 12707728945743425350 #ifdef ENABLE_API_FUNCTIONS int initialize_system(void); @@ -301,6 +310,7 @@ void deprecated_cleanup(struct DeprecatedStruct* data); #endif // ENABLE_API_FUNCTIONS ---------------------------------------- +(IN-ACTIVE) 3898577154630875261 #else // Fallback when advanced features are disabled @@ -430,6 +440,7 @@ legacy_configs 16188692334800662690 #if defined(VERSION_2) || defined(VERSION_3) ---------------------------------------- +(IN-ACTIVE) 4317565735544191414 #ifdef ENABLE_LOGGING LogLevel default_log_level; @@ -441,6 +452,7 @@ legacy_configs 7316820520996380391 #ifdef ENABLE_DATA_PROCESSING ---------------------------------------- +(IN-ACTIVE) 8858807332175181956 #ifdef DEBUG_MODE extended_int_t access_count; @@ -450,6 +462,7 @@ legacy_configs 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 4028265945927569883 #ifdef ENABLE_MATH_UTILS struct Calculator { @@ -468,6 +481,7 @@ INLINE extended_float_t calculate(extended_int_t a, extended_int_t b) { } #endif ---------------------------------------- +(IN-ACTIVE) 15971896135806486621 #ifdef ENABLE_NETWORKING struct Connection { @@ -512,6 +526,7 @@ void initialize_connections() { 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 3283656231603163010 #ifdef OLD_API_VERSION struct OldHandle { @@ -531,6 +546,7 @@ struct OldApiData old_api_instances[3] = { }; #endif ---------------------------------------- +(IN-ACTIVE) 13171204464568943967 #ifdef EXPERIMENTAL_FEATURES struct ExperimentalData { @@ -548,6 +564,7 @@ struct ExperimentalConfig experimental_config = { }; #endif ---------------------------------------- +(IN-ACTIVE) 12988119784036996851 #ifdef BETA_FUNCTIONS struct BetaConfig { @@ -566,6 +583,7 @@ struct BetaData beta_data_instance = { }; #endif ---------------------------------------- +(IN-ACTIVE) 6896127565158225636 #ifdef DEPRECATED_SUPPORT struct DeprecatedBuffer { @@ -582,6 +600,7 @@ struct DeprecatedStruct deprecated_instance = { }; #endif ---------------------------------------- +(IN-ACTIVE) 15736156447599205530 #ifdef ENABLE_API_FUNCTIONS int initialize_system(void); @@ -637,6 +656,7 @@ void deprecated_cleanup(struct DeprecatedStruct* data); #endif // ENABLE_API_FUNCTIONS ---------------------------------------- +(IN-ACTIVE) 3898577154630875261 #else // Fallback when advanced features are disabled diff --git a/src/tests/armor/functional/comments_update/expected_output.txt b/src/tests/armor/functional/comments_update/expected_output.txt index 6bca771..c0eb99a 100644 --- a/src/tests/armor/functional/comments_update/expected_output.txt +++ b/src/tests/armor/functional/comments_update/expected_output.txt @@ -408,6 +408,7 @@ inline const char* get_platform() { 15219203312359965853 #include ---------------------------------------- +(IN-ACTIVE) 2645106581235657280 #ifdef _WIN32 // Check if compiling on Windows #define MYLIB_PLATFORM "Windows" // Set platform string for Windows @@ -417,11 +418,13 @@ inline const char* get_platform() { 13241275756687149476 #define MYLIB_PLATFORM "Linux" ---------------------------------------- +(IN-ACTIVE) 12227028146242223846 #else // All other platforms #define MYLIB_PLATFORM "Unknown" // Default platform string #endif // End platform detection ---------------------------------------- +(IN-ACTIVE) 2957985788965770067 #ifdef NDEBUG // If in release mode (NDEBUG defined) #define MYLIB_LOG(msg) // Empty macro - no logging in release @@ -906,6 +909,7 @@ inline const char* get_platform() { 15219203312359965853 #include ---------------------------------------- +(IN-ACTIVE) 2645106581235657280 #ifdef _WIN32 // Check if compiling on Windows #define MYLIB_PLATFORM "Windows" // Set platform string for Windows @@ -915,11 +919,13 @@ inline const char* get_platform() { 13241275756687149476 #define MYLIB_PLATFORM "Linux" ---------------------------------------- +(IN-ACTIVE) 12227028146242223846 #else // All other platforms #define MYLIB_PLATFORM "Unknown" // Default platform string #endif // End platform detection ---------------------------------------- +(IN-ACTIVE) 2957985788965770067 #ifdef NDEBUG // If in release mode (NDEBUG defined) #define MYLIB_LOG(msg) // Empty macro - no logging in release diff --git a/src/tests/armor/functional/conftest.py b/src/tests/armor/functional/conftest.py index 3265233..176fce7 100644 --- a/src/tests/armor/functional/conftest.py +++ b/src/tests/armor/functional/conftest.py @@ -1,6 +1,5 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import pytest @@ -31,6 +30,14 @@ def binary_args(request): prj_root2 = os.path.join(curr_dir, "v2") return [prj_root1, prj_root2, "mylib.h", "--dump-ast-diff", "-r", "json"] +@pytest.fixture +def binary_args_c(request): + """Returns the arguments for the binary with debug and JSON output enabled.""" + curr_dir = os.path.dirname(request.fspath) + prj_root1 = os.path.join(curr_dir, "v1") + prj_root2 = os.path.join(curr_dir, "v2") + return [prj_root1, prj_root2, "mylib.h", "--lang", "c", "--dump-ast-diff", "-r", "json"] + @pytest.fixture def dependent_binary_args(request): """Returns the arguments for the binary with debug and JSON output enabled.""" diff --git a/src/tests/armor/functional/inactive_code_update/expected_output.txt b/src/tests/armor/functional/inactive_code_update/expected_output.txt index ad81ec1..bef3530 100644 --- a/src/tests/armor/functional/inactive_code_update/expected_output.txt +++ b/src/tests/armor/functional/inactive_code_update/expected_output.txt @@ -300,6 +300,7 @@ processData MYLIB_TOSTRING(MYLIB_VERSION_MINOR) "." \ MYLIB_TOSTRING(MYLIB_VERSION_PATCH) ---------------------------------------- +(IN-ACTIVE) 10789492512563752263 #if defined(_WIN32) || defined(_WIN64) #define MYLIB_PLATFORM_WINDOWS @@ -313,6 +314,7 @@ processData 6656654353517034452 #define MYLIB_EXPORT __attribute__((visibility("default"))) ---------------------------------------- +(IN-ACTIVE) 6094473516132133165 #elif defined(__APPLE__) && defined(__MACH__) #define MYLIB_PLATFORM_MACOS @@ -331,6 +333,7 @@ processData 15464300162902583911 #define MYLIB_FORCE_INLINE __attribute__((always_inline)) inline ---------------------------------------- +(IN-ACTIVE) 15455234452876028348 #elif defined(__GNUC__) #define MYLIB_COMPILER_GCC @@ -343,6 +346,7 @@ processData #define MYLIB_FORCE_INLINE inline #endif ---------------------------------------- +(IN-ACTIVE) 12074142667616033186 #ifdef MYLIB_DEBUG #define MYLIB_DEBUG_MODE 1 @@ -363,6 +367,7 @@ processData 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 5276368870544460156 #ifdef MYLIB_ENABLE_THREADING #define MYLIB_THREAD_SAFE 1 @@ -390,6 +395,7 @@ processData 8717144482643656166 #define MYLIB_UNUSED(x) ((void)(x)) ---------------------------------------- +(IN-ACTIVE) 609842095911410082 #ifdef MYLIB_COMPILER_MSVC #define MYLIB_ALIGN(n) __declspec(align(n)) @@ -416,12 +422,14 @@ processData 10705571255276855191 #ifdef __cplusplus ---------------------------------------- +(IN-ACTIVE) 16125242627147687389 #ifdef MYLIB_DEBUG MYLIB_ASSERT(data != nullptr, "Data pointer is null"); printf("[DEBUG] Processing %zu bytes, Flags: 0x%X\n", size, flags); #endif ---------------------------------------- +(IN-ACTIVE) 17793810274277806111 #ifdef MYLIB_PLATFORM_WINDOWS void* buffer = _aligned_malloc(size, 16); @@ -431,11 +439,13 @@ processData #elif defined(MYLIB_PLATFORM_LINUX) ---------------------------------------- +(IN-ACTIVE) 11780944220859629953 #ifdef MYLIB_COMPILER_GCC __builtin_prefetch(buffer, 0, 3); #endif ---------------------------------------- +(IN-ACTIVE) 4186474104662229156 #elif defined(MYLIB_PLATFORM_MACOS) void* buffer = nullptr; @@ -444,6 +454,7 @@ processData void* buffer = malloc(size); #endif ---------------------------------------- +(IN-ACTIVE) 9496518772622674977 #ifdef MYLIB_ENABLE_THREADING static std::mutex mtx; @@ -455,23 +466,27 @@ processData 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 13513711463712273799 #ifdef MYLIB_COMPILER_MSVC result += 100; #elif defined(MYLIB_COMPILER_GCC) || defined(MYLIB_COMPILER_CLANG) ---------------------------------------- +(IN-ACTIVE) 5228759663874785823 #else result += 300; #endif ---------------------------------------- +(IN-ACTIVE) 17535763163473717893 #ifdef MYLIB_PLATFORM_WINDOWS result += 1000; #elif defined(MYLIB_PLATFORM_LINUX) ---------------------------------------- +(IN-ACTIVE) 9182786835064148631 #elif defined(MYLIB_PLATFORM_MACOS) result += 3000; @@ -479,6 +494,7 @@ processData result += 4000; #endif ---------------------------------------- +(IN-ACTIVE) 5400481813736155076 #ifdef MYLIB_DEBUG static size_t totalBytes = 0; @@ -486,6 +502,7 @@ processData printf("[DEBUG] Total processed: %zu bytes, Result: %d\n", totalBytes, result); #endif ---------------------------------------- +(IN-ACTIVE) 7535211014293608317 #ifdef MYLIB_PLATFORM_WINDOWS _aligned_free(buffer); @@ -497,6 +514,7 @@ processData 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 9534294440999257955 #ifdef MYLIB_COMPILER_MSVC #define MYLIB_LIKELY(x) (x) @@ -506,6 +524,7 @@ processData 10585965716865580119 #define MYLIB_LIKELY(x) __builtin_expect(!!(x), 1) ---------------------------------------- +(IN-ACTIVE) 6262325460462613483 #else #define MYLIB_LIKELY(x) (x) @@ -832,6 +851,7 @@ processData MYLIB_TOSTRING(MYLIB_VERSION_MINOR) "." \ MYLIB_TOSTRING(MYLIB_VERSION_PATCH) ---------------------------------------- +(IN-ACTIVE) 10789492512563752263 #if defined(_WIN32) || defined(_WIN64) #define MYLIB_PLATFORM_WINDOWS @@ -845,6 +865,7 @@ processData 6656654353517034452 #define MYLIB_EXPORT __attribute__((visibility("default"))) ---------------------------------------- +(IN-ACTIVE) 6094473516132133165 #elif defined(__APPLE__) && defined(__MACH__) #define MYLIB_PLATFORM_MACOS @@ -863,6 +884,7 @@ processData 15464300162902583911 #define MYLIB_FORCE_INLINE __attribute__((always_inline)) inline ---------------------------------------- +(IN-ACTIVE) 15455234452876028348 #elif defined(__GNUC__) #define MYLIB_COMPILER_GCC @@ -875,6 +897,7 @@ processData #define MYLIB_FORCE_INLINE inline #endif ---------------------------------------- +(IN-ACTIVE) 12074142667616033186 #ifdef MYLIB_DEBUG #define MYLIB_DEBUG_MODE 1 @@ -895,6 +918,7 @@ processData 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 5276368870544460156 #ifdef MYLIB_ENABLE_THREADING #define MYLIB_THREAD_SAFE 1 @@ -922,6 +946,7 @@ processData 8717144482643656166 #define MYLIB_UNUSED(x) ((void)(x)) ---------------------------------------- +(IN-ACTIVE) 609842095911410082 #ifdef MYLIB_COMPILER_MSVC #define MYLIB_ALIGN(n) __declspec(align(n)) @@ -948,12 +973,14 @@ processData 10705571255276855191 #ifdef __cplusplus ---------------------------------------- +(IN-ACTIVE) 16125242627147687389 #ifdef MYLIB_DEBUG MYLIB_ASSERT(data != nullptr, "Data pointer is null"); printf("[DEBUG] Processing %zu bytes, Flags: 0x%X\n", size, flags); #endif ---------------------------------------- +(IN-ACTIVE) 17793810274277806111 #ifdef MYLIB_PLATFORM_WINDOWS void* buffer = _aligned_malloc(size, 16); @@ -963,11 +990,13 @@ processData #elif defined(MYLIB_PLATFORM_LINUX) ---------------------------------------- +(IN-ACTIVE) 11780944220859629953 #ifdef MYLIB_COMPILER_GCC __builtin_prefetch(buffer, 0, 3); #endif ---------------------------------------- +(IN-ACTIVE) 4186474104662229156 #elif defined(MYLIB_PLATFORM_MACOS) void* buffer = nullptr; @@ -976,6 +1005,7 @@ processData void* buffer = malloc(size); #endif ---------------------------------------- +(IN-ACTIVE) 9496518772622674977 #ifdef MYLIB_ENABLE_THREADING static std::mutex mtx; @@ -987,23 +1017,27 @@ processData 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 13513711463712273799 #ifdef MYLIB_COMPILER_MSVC result += 100; #elif defined(MYLIB_COMPILER_GCC) || defined(MYLIB_COMPILER_CLANG) ---------------------------------------- +(IN-ACTIVE) 5228759663874785823 #else result += 300; #endif ---------------------------------------- +(IN-ACTIVE) 17535763163473717893 #ifdef MYLIB_PLATFORM_WINDOWS result += 1000; #elif defined(MYLIB_PLATFORM_LINUX) ---------------------------------------- +(IN-ACTIVE) 9182786835064148631 #elif defined(MYLIB_PLATFORM_MACOS) result += 3000; @@ -1011,6 +1045,7 @@ processData result += 4000; #endif ---------------------------------------- +(IN-ACTIVE) 6554561820803661536 #ifdef MYLIB_DEBUG #define NEW_LIB @@ -1019,6 +1054,7 @@ processData printf("[DEBUG] Total processed: %zu bytes, Result: %d\n", totalBytes, result); #endif ---------------------------------------- +(IN-ACTIVE) 7535211014293608317 #ifdef MYLIB_PLATFORM_WINDOWS _aligned_free(buffer); @@ -1030,6 +1066,7 @@ processData 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 9707649004395554193 #ifdef MYLIB_COMPILER_MSVC #define MYLIB_LIKELY(x) (x) @@ -1040,6 +1077,7 @@ processData 10585965716865580119 #define MYLIB_LIKELY(x) __builtin_expect(!!(x), 1) ---------------------------------------- +(IN-ACTIVE) 6262325460462613483 #else #define MYLIB_LIKELY(x) (x) diff --git a/src/tests/armor/functional/lang_options/expected_output.json b/src/tests/armor/functional/lang_options/expected_output.json new file mode 100644 index 0000000..76458a1 --- /dev/null +++ b/src/tests/armor/functional/lang_options/expected_output.json @@ -0,0 +1,534 @@ +{ + "astDiff": [ + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_new::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_new::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_new::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_new", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_delete::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_delete::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_delete::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_delete", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_namespace::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_namespace::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_namespace::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_namespace", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_cast::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_cast::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_cast::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_cast", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_reinterpret::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_reinterpret::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_reinterpret::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_reinterpret", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_bool::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_bool::2" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_bool::3" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_bool::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_bool", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_try::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_try::2" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_try::3" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_try::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_try", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_access::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_access::2" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_access::3" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_access::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_access", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_misc::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_misc::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_misc::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_misc", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_operator::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_operator::2" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_operator::3" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_operator::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_operator", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_override::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fun_override::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fun_override::(ReturnType)" + } + ], + "nodeType": "Function", + "qualifiedName": "fun_override", + "tag": "removed" + }, + { + "children": [ + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::new" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::delete" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::class" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::this" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::virtual" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::explicit" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::operator" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::friend" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::namespace" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::template" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::override" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::final" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::true" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::false" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::bool" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::try" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::catch" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::throw" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::public" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::private" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::protected" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::static_cast" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::dynamic_cast" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::reinterpret_cast" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::const_cast" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::typeid" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::typename" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::using" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::export" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::mutable" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::noexcept" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::constexpr" + }, + { + "dataType": "int", + "nodeType": "Field", + "qualifiedName": "cpp_keywords_as_fields_t::decltype" + } + ], + "nodeType": "Struct", + "qualifiedName": "cpp_keywords_as_fields_t", + "tag": "removed" + }, + { + "dataType": "struct cpp_keywords_as_fields_t", + "nodeType": "Typedef", + "qualifiedName": "cpp_keywords_as_fields_t", + "tag": "removed" + }, + { + "children": [ + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fn_new_t::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fn_new_t::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fn_new_t::(ReturnType)" + } + ], + "dataType": "*", + "nodeType": "FunctionPointer", + "qualifiedName": "fn_new_t" + } + ], + "nodeType": "Typedef", + "qualifiedName": "fn_new_t", + "tag": "removed" + }, + { + "children": [ + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fn_class_t::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fn_class_t::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fn_class_t::(ReturnType)" + } + ], + "dataType": "*", + "nodeType": "FunctionPointer", + "qualifiedName": "fn_class_t" + } + ], + "nodeType": "Typedef", + "qualifiedName": "fn_class_t", + "tag": "removed" + }, + { + "children": [ + { + "children": [ + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fn_virtual_t::1" + }, + { + "dataType": "int", + "nodeType": "Parameter", + "qualifiedName": "fn_virtual_t::2" + }, + { + "dataType": "int", + "nodeType": "ReturnType", + "qualifiedName": "fn_virtual_t::(ReturnType)" + } + ], + "dataType": "*", + "nodeType": "FunctionPointer", + "qualifiedName": "fn_virtual_t" + } + ], + "nodeType": "Typedef", + "qualifiedName": "fn_virtual_t", + "tag": "removed" + } + ], + "headerResolutionFailures": [], + "parsed_status": 2, + "unparsed_status": 0 +} \ No newline at end of file diff --git a/src/tests/armor/functional/lang_options/test_lang_options.py b/src/tests/armor/functional/lang_options/test_lang_options.py new file mode 100644 index 0000000..f37405a --- /dev/null +++ b/src/tests/armor/functional/lang_options/test_lang_options.py @@ -0,0 +1,30 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +import os +import json +import subprocess +from deepdiff import DeepDiff + + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json, ignore_order=True) + + assert diff == {} + diff --git a/src/tests/armor/functional/lang_options/v1/mylib.h b/src/tests/armor/functional/lang_options/v1/mylib.h new file mode 100644 index 0000000..63b9915 --- /dev/null +++ b/src/tests/armor/functional/lang_options/v1/mylib.h @@ -0,0 +1,70 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause +/* Test header: valid C code using C++ keywords as identifiers */ + +/* --- Functions using C++ keywords as parameter names --- */ + +/* access/object keywords */ +int fun_new(int new, int this); +int fun_delete(int delete, int class); +int fun_namespace(int namespace, int template); + +/* type/cast keywords */ +int fun_cast(int static_cast, int dynamic_cast); +int fun_reinterpret(int reinterpret_cast, int const_cast); + +/* boolean/logic keywords */ +int fun_bool(int true, int false, int bool); + +/* exception keywords */ +int fun_try(int try, int catch, int throw); + +/* access specifier keywords */ +int fun_access(int public, int private, int protected); + +/* other C++ keywords */ +int fun_misc(int virtual, int explicit); +int fun_operator(int operator, int friend, int using); +int fun_override(int override, int final); + +/* --- Variables using C++ keywords as names --- */ +typedef struct { + int new; + int delete; + int class; + int this; + int virtual; + int explicit; + int operator; + int friend; + int namespace; + int template; + int override; + int final; + int true; + int false; + int bool; + int try; + int catch; + int throw; + int public; + int private; + int protected; + int static_cast; + int dynamic_cast; + int reinterpret_cast; + int const_cast; + int typeid; + int typename; + int using; + int export; + int mutable; + int noexcept; + int constexpr; + int decltype; +} cpp_keywords_as_fields_t; + +/* --- Function pointers --- */ +typedef int (*fn_new_t)(int new, int delete); +typedef int (*fn_class_t)(int class, int namespace); +typedef int (*fn_virtual_t)(int virtual, int override); \ No newline at end of file diff --git a/src/tests/armor/functional/lang_options/v2/mylib.h b/src/tests/armor/functional/lang_options/v2/mylib.h new file mode 100644 index 0000000..5f15b0c --- /dev/null +++ b/src/tests/armor/functional/lang_options/v2/mylib.h @@ -0,0 +1,2 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/armor/functional/no_diff_exit_code/test_no_diff_exit_code.py b/src/tests/armor/functional/no_diff_exit_code/test_no_diff_exit_code.py new file mode 100644 index 0000000..57181b9 --- /dev/null +++ b/src/tests/armor/functional/no_diff_exit_code/test_no_diff_exit_code.py @@ -0,0 +1,21 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +import os +import json +import subprocess +from deepdiff import DeepDiff + + +def test_ast_diff(binary_path, binary_args, request): + + test_dir = os.path.dirname(request.fspath) + + result = subprocess.run( + [binary_path] + binary_args, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + assert result.returncode == 0 + \ No newline at end of file diff --git a/src/tests/armor/functional/no_diff_exit_code/v1/mylib.h b/src/tests/armor/functional/no_diff_exit_code/v1/mylib.h new file mode 100644 index 0000000..63d9c80 --- /dev/null +++ b/src/tests/armor/functional/no_diff_exit_code/v1/mylib.h @@ -0,0 +1,6 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause +struct alpha{ + int a; + int b; +}; diff --git a/src/tests/armor/functional/no_diff_exit_code/v2/mylib.h b/src/tests/armor/functional/no_diff_exit_code/v2/mylib.h new file mode 100644 index 0000000..63d9c80 --- /dev/null +++ b/src/tests/armor/functional/no_diff_exit_code/v2/mylib.h @@ -0,0 +1,6 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause +struct alpha{ + int a; + int b; +}; diff --git a/src/tests/armor/functional/unsupported_code_update/test_supported_code_update.py b/src/tests/armor/functional/unsupported_code_update/test_unsupported_code_update.py similarity index 100% rename from src/tests/armor/functional/unsupported_code_update/test_supported_code_update.py rename to src/tests/armor/functional/unsupported_code_update/test_unsupported_code_update.py diff --git a/src/tests/armor/src/options_handler.cpp b/src/tests/armor/src/options_handler.cpp index b257931..d550956 100644 --- a/src/tests/armor/src/options_handler.cpp +++ b/src/tests/armor/src/options_handler.cpp @@ -18,6 +18,20 @@ #define TOOL_VERSION "" #endif +LANG_OPTIONS stringToLangOption(const std::string& lang) { + // Convert to lowercase for case-insensitive comparison + std::string lowerLang = lang; + std::transform(lowerLang.begin(), lowerLang.end(), lowerLang.begin(), + [](unsigned char c){ return std::tolower(c); }); + + if (lowerLang == LANG_C) { + return LANG_OPTIONS::C; + } else if (lowerLang == LANG_CPP) { + return LANG_OPTIONS::CPP; + } + return LANG_OPTIONS::CPP; // default to C++ +} + bool filesAreDifferentUsingDiff(const std::string &file1, const std::string &file2) { std::string command = "diff -q " + file1 + " " + file2 + " > /dev/null"; return std::system(command.c_str()) != 0; @@ -30,6 +44,7 @@ bool runArmorTool(int argc, const char **argv) { std::vector headers; std::string headerSubDir; std::string reportFormat = "html"; + std::string language = LANG_CPP; // default to C++ bool dumpAstDiff = false; std::string debugLevel = ""; std::vector IncludePaths; @@ -61,6 +76,9 @@ bool runArmorTool(int argc, const char **argv) { app.add_option("--report-format,-r", reportFormat, "Report format: html (default).\n" "If json is provided, both html and json reports will be generated.") ->check(CLI::IsMember({"html", "json"})); + app.add_option("--lang,-l", language, "Language mode: cpp (default) or c.\n" + "Use 'c' for C headers, 'cpp' for C++ headers.") + ->transform(CLI::IsMember({LANG_C, LANG_CPP}, CLI::ignore_case)); app.add_flag("--dump-ast-diff", dumpAstDiff, "Dump AST diff JSON files for debugging"); app.set_version_flag("--version,-v", TOOL_VERSION); app.add_option("--log-level", debugLevel, "Set debug log level: ERROR, LOG, INFO (default), DEBUG") @@ -102,6 +120,10 @@ bool runArmorTool(int argc, const char **argv) { debugConfig.setLevel(DebugConfig::Level::NONE); } + // Convert language string to LANG_OPTIONS enum + LANG_OPTIONS langOption = stringToLangOption(language); + armor::info() << "Language mode set to: " << language << "\n"; + bool processed = false; std::vector headersToCompare; if (!headers.empty()) { @@ -110,31 +132,36 @@ bool runArmorTool(int argc, const char **argv) { if (!headerSubDir.empty()) { file1 = projectRoot1 + "/" + headerSubDir + "/" + header; file2 = projectRoot2 + "/" + headerSubDir + "/" + header; - } else { + } + else { file1 = projectRoot1 + "/" + header; file2 = projectRoot2 + "/" + header; } armor::user_print() << "Processing files: " << file1 << " " << file2 << "\n"; if (!std::filesystem::exists(file1)) { armor::user_error() << "Missing header in older version: " << file1 << "\n"; - } else if (!std::filesystem::exists(file2)) { + } + else if (!std::filesystem::exists(file2)) { armor::user_error() << "Missing header in newer version: " << file2 << "\n"; - } else if (filesAreDifferentUsingDiff(file1, file2)) { + } + else if (filesAreDifferentUsingDiff(file1, file2)) { PARSING_STATUS parsingStatus = processHeaderPairAlpha(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); + IncludePaths, macros, langOption); switch (parsingStatus) { case NO_FATAL_ERRORS: armor::info() << "Processing Headers again via beta parser\n"; processHeaderPairBeta(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); + IncludePaths, macros, langOption); break; case FATAL_ERRORS: armor::info() << "Processing Headers stopped at alpha parser\n"; break; } processed = true; - } else { + } + else { armor::user_print() << "No differences found between: " << file1 << " and " << file2 << "\n"; + return true; } } } @@ -156,24 +183,28 @@ bool runArmorTool(int argc, const char **argv) { armor::user_print() << "Processing files: " << file1 << " " << file2 << "\n"; if (!std::filesystem::exists(file1)) { armor::user_error() << "Missing header in older version: " << file1 << "\n"; - } else if (!std::filesystem::exists(file2)) { + } + else if (!std::filesystem::exists(file2)) { armor::user_error() << "Missing header in newer version: " << file2 << "\n"; - } else if (filesAreDifferentUsingDiff(file1, file2)) { + } + else if (filesAreDifferentUsingDiff(file1, file2)) { PARSING_STATUS parsingStatus = processHeaderPairAlpha(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); + IncludePaths, macros, langOption); switch (parsingStatus) { case NO_FATAL_ERRORS: armor::info() << "Processing Headers again via v2\n"; processHeaderPairBeta(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); + IncludePaths, macros, langOption); break; case FATAL_ERRORS: armor::info() << "Processing Headers stopped at v1\n"; break; } processed = true; - } else { + } + else { armor::user_print() << "No differences found between: " << file1 << " and " << file2 << "\n"; + return true; } } } diff --git a/src/tests/beta/__init__.py b/src/tests/beta/__init__.py new file mode 100644 index 0000000..dc3cadc --- /dev/null +++ b/src/tests/beta/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +# Make beta directory a Python package \ No newline at end of file diff --git a/src/tests/beta/functional/__init__.py b/src/tests/beta/functional/__init__.py new file mode 100644 index 0000000..d920b29 --- /dev/null +++ b/src/tests/beta/functional/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +# Make functional directory a Python package \ No newline at end of file diff --git a/src/tests/beta/functional/anonymous_type_changes/__init__.py b/src/tests/beta/functional/anonymous_type_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/anonymous_type_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/canonical_type_changes/__init__.py b/src/tests/beta/functional/canonical_type_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/canonical_type_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/canonical_type_changes/test_beta_canonical_type_changes.py b/src/tests/beta/functional/canonical_type_changes/test_beta_canonical_type_changes.py index 7742917..a68ad16 100644 --- a/src/tests/beta/functional/canonical_type_changes/test_beta_canonical_type_changes.py +++ b/src/tests/beta/functional/canonical_type_changes/test_beta_canonical_type_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/beta/functional/class_changes/__init__.py b/src/tests/beta/functional/class_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/class_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/conftest.py b/src/tests/beta/functional/conftest.py index cb88051..d33bbfe 100644 --- a/src/tests/beta/functional/conftest.py +++ b/src/tests/beta/functional/conftest.py @@ -1,6 +1,5 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import pytest @@ -30,3 +29,12 @@ def binary_args(request): prj_root1 = os.path.join(curr_dir, "v1") prj_root2 = os.path.join(curr_dir, "v2") return [prj_root1, prj_root2, "mylib.h", "--dump-ast-diff", "-r", "json"] + +@pytest.fixture +def binary_args_c(request): + """Returns the arguments for the binary with debug and JSON output enabled.""" + curr_dir = os.path.dirname(request.fspath) + prj_root1 = os.path.join(curr_dir, "v1") + prj_root2 = os.path.join(curr_dir, "v2") + return [prj_root1, prj_root2, "mylib.h", "--lang", "c", "--dump-ast-diff", "-r", "json"] + diff --git a/src/tests/beta/functional/enum_changes/__init__.py b/src/tests/beta/functional/enum_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/enum_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/enum_changes/test_beta_enum_changes.py b/src/tests/beta/functional/enum_changes/test_beta_enum_changes.py index 7742917..eaae678 100644 --- a/src/tests/beta/functional/enum_changes/test_beta_enum_changes.py +++ b/src/tests/beta/functional/enum_changes/test_beta_enum_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} diff --git a/src/tests/beta/functional/flow_cxx_adv_template_categorisation/expected_output.txt b/src/tests/beta/functional/flow_cxx_adv_template_categorisation/expected_output.txt index a467dde..6d5a9f0 100644 --- a/src/tests/beta/functional/flow_cxx_adv_template_categorisation/expected_output.txt +++ b/src/tests/beta/functional/flow_cxx_adv_template_categorisation/expected_output.txt @@ -788,6 +788,7 @@ typename AdvancedContainerWrapper::iterator global_iterator 11799315910370350277 #include ---------------------------------------- +(IN-ACTIVE) 12832985064113139461 #if __cplusplus >= 202002L #include @@ -824,6 +825,7 @@ void print_container(const C& container) { #endif // C++20 ---------------------------------------- +(IN-ACTIVE) 4552978047279620817 #if __cplusplus >= 202002L && __has_include() #include diff --git a/src/tests/beta/functional/flow_cxx_adv_template_categorisation/test_adv_template_categorisation.py b/src/tests/beta/functional/flow_cxx_adv_template_categorisation/test_adv_template_categorisation.py index a2e5155..9262230 100644 --- a/src/tests/beta/functional/flow_cxx_adv_template_categorisation/test_adv_template_categorisation.py +++ b/src/tests/beta/functional/flow_cxx_adv_template_categorisation/test_adv_template_categorisation.py @@ -1,5 +1,5 @@ -# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause import os import json import subprocess diff --git a/src/tests/beta/functional/flow_cxx_forward_decl_categorisation/expected_output.txt b/src/tests/beta/functional/flow_cxx_forward_decl_categorisation/expected_output.txt index e484c7a..0d65e24 100644 --- a/src/tests/beta/functional/flow_cxx_forward_decl_categorisation/expected_output.txt +++ b/src/tests/beta/functional/flow_cxx_forward_decl_categorisation/expected_output.txt @@ -738,6 +738,7 @@ namespace literals { 11529733944468691740 #ifdef __GNUC__ ---------------------------------------- +(IN-ACTIVE) 2075011373704711983 #elif defined(_MSC_VER) return __FUNCSIG__; diff --git a/src/tests/beta/functional/flow_macros_categorisation/expected_output.txt b/src/tests/beta/functional/flow_macros_categorisation/expected_output.txt index c81a7e2..2944b57 100644 --- a/src/tests/beta/functional/flow_macros_categorisation/expected_output.txt +++ b/src/tests/beta/functional/flow_macros_categorisation/expected_output.txt @@ -497,6 +497,7 @@ 4697782646697335805 #define STRINGIFY_EXPANDED(x) STRINGIFY(x) ---------------------------------------- +(IN-ACTIVE) 6477139945075972597 #ifdef DEBUG #define DEBUG_PRINT(x) printf("DEBUG: %s = %d\n", #x, (x)) /* Print variable name and value */ @@ -536,6 +537,7 @@ 9329535409317421868 #define COMBINED(x, y) CONCAT(STRINGIFY(x), STRINGIFY(y)) ---------------------------------------- +(IN-ACTIVE) 13248869921311003807 #if defined(FEATURE_A) && !defined(FEATURE_B) // first comments #define FEATURE_FLAG 1 /* FEATURE_A has priority when FEATURE_B is not defined */ @@ -549,6 +551,7 @@ 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 9474032327556054008 #ifdef _WIN32 #define PLATFORM_SPECIFIC_FUNCTION() win32_function() /* Windows implementation */ @@ -560,6 +563,7 @@ 15838141755185658721 #define PLATFORM_SPECIFIC_FUNCTION() linux_function() ---------------------------------------- +(IN-ACTIVE) 13005075863160176067 #else #define PLATFORM_SPECIFIC_FUNCTION() generic_function() /* Fallback for unknown platforms */ @@ -571,6 +575,7 @@ 8192046942710364384 #define INLINE_ASM(asm_code) __asm__(asm_code) ---------------------------------------- +(IN-ACTIVE) 1074130666721733757 #else #define INLINE_ASM(asm_code) /* No-op for unsupported compilers */ @@ -582,6 +587,7 @@ 9006691468829543498 #define DEPRECATED(func) func __attribute__((deprecated)) ---------------------------------------- +(IN-ACTIVE) 1417698705628194800 #elif defined(_MSC_VER) #define DEPRECATED(func) __declspec(deprecated) func /* MSVC declspec */ @@ -598,6 +604,7 @@ 11777811773461686288 #define UNLIKELY(x) __builtin_expect(!!(x), 0) ---------------------------------------- +(IN-ACTIVE) 4598632821586868593 #else #define LIKELY(x) (x) /* No optimization hints */ @@ -649,6 +656,7 @@ 17786182838777062169 #define SWAP(a, b) do { typeof(a) temp = (a); (a) = (b); (b) = temp; } while(0) ---------------------------------------- +(IN-ACTIVE) 14637574784572251887 #if 0 void inactive_function() { @@ -657,6 +665,7 @@ void inactive_function() { } #endif ---------------------------------------- +(IN-ACTIVE) 677721035919619419 #ifdef UNDEFINED_MACRO void another_inactive_function() { @@ -677,6 +686,7 @@ void another_inactive_function() { 17696063952571374410 #define CONDITION_MET 1 ---------------------------------------- +(IN-ACTIVE) 13404186847818870969 #else #define CONDITION_MET 0 /* Condition is not met */ @@ -691,6 +701,7 @@ void another_inactive_function() { 2695351812175255397 #define FEATURE_X_ENABLED 1 ---------------------------------------- +(IN-ACTIVE) 9952196420283705679 #elifndef LEGACY_FEATURE_Y /* C23: "else if not defined" */ #define NEW_FEATURE_X 200 /* Fallback value when legacy feature is undefined */ @@ -699,6 +710,7 @@ void another_inactive_function() { #define NEW_FEATURE_X 0 /* Disabled state */ #endif ---------------------------------------- +(IN-ACTIVE) 2332983418588728947 #ifdef EXPERIMENTAL_MODE // Checking #ifdef #define EXPERIMENTAL_VALUE 999 /* High value for experimental mode */ @@ -714,6 +726,7 @@ void another_inactive_function() { 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 12621194928058068335 #ifdef LEVEL_1 #ifdef LEVEL_2 @@ -779,6 +792,7 @@ void another_inactive_function() { 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 12765956525272886779 #if 0 #if 1 @@ -789,6 +803,7 @@ void another_inactive_function() { #endif #endif ---------------------------------------- +(IN-ACTIVE) 3096152467939040003 #if defined(COMPILER_GCC) #define COMPILER_NAME "GCC" @@ -831,6 +846,7 @@ void another_inactive_function() { 15897096405968499044 #define TEMP_VALUE 300 ---------------------------------------- +(IN-ACTIVE) 17774308999468338591 #if (defined(VERSION_MAJOR) && VERSION_MAJOR >= 2) || \ (defined(VERSION_MAJOR) && VERSION_MAJOR == 1 && \ @@ -861,6 +877,7 @@ void another_inactive_function() { 2593811414999902229 #define ARCH_NAME "x86_64" ---------------------------------------- +(IN-ACTIVE) 1465112240517950697 #ifdef __AVX2__ #define HAS_AVX2 1 @@ -872,6 +889,7 @@ void another_inactive_function() { 13425203279752416008 #endif ---------------------------------------- +(IN-ACTIVE) 10236021034342675268 #elif defined(__i386__) || defined(_M_IX86) // ok #define ARCH_64BIT 0 @@ -887,6 +905,7 @@ void another_inactive_function() { #define HAS_AVX2 0 #endif ---------------------------------------- +(IN-ACTIVE) 16814099614349482556 #ifdef USE_CUSTOM_ALLOCATOR #define ALLOC(size) custom_alloc(size) /* Use custom memory allocator */ @@ -921,6 +940,7 @@ void another_inactive_function() { 13473011240135753077 #define CAT4(a, b, c, d) CAT(CAT3(a, b, c), d) ---------------------------------------- +(IN-ACTIVE) 4769135047991098008 #ifdef INCLUDE_EXTRA_FEATURES #define EXTRA_FEATURE_1 1 /* Enable first extra feature */ @@ -948,6 +968,7 @@ void another_inactive_function() { 12861544208380990568 #undef TEMP_C ---------------------------------------- +(IN-ACTIVE) 3656396748884955610 #ifdef _MSC_VER #define PRAGMA_PACK_PUSH _Pragma("pack(push, 1)") /* MSVC: Push current packing, set to 1 byte */ diff --git a/src/tests/beta/functional/function_changes/__init__.py b/src/tests/beta/functional/function_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/function_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/function_changes/test_beta_function_changes.py b/src/tests/beta/functional/function_changes/test_beta_function_changes.py index 7742917..a68ad16 100644 --- a/src/tests/beta/functional/function_changes/test_beta_function_changes.py +++ b/src/tests/beta/functional/function_changes/test_beta_function_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/beta/functional/function_pointer_changes/__init__.py b/src/tests/beta/functional/function_pointer_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/function_pointer_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/global_variable_changes/__init__.py b/src/tests/beta/functional/global_variable_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/global_variable_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/global_variable_changes/test_beta_global_variable_changes.py b/src/tests/beta/functional/global_variable_changes/test_beta_global_variable_changes.py index 7742917..a68ad16 100644 --- a/src/tests/beta/functional/global_variable_changes/test_beta_global_variable_changes.py +++ b/src/tests/beta/functional/global_variable_changes/test_beta_global_variable_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/beta/functional/sanity/__init__.py b/src/tests/beta/functional/sanity/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/sanity/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/struct_changes/__init__.py b/src/tests/beta/functional/struct_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/struct_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/struct_changes/test_beta_struct_changes.py b/src/tests/beta/functional/struct_changes/test_beta_struct_changes.py index 7742917..eaae678 100644 --- a/src/tests/beta/functional/struct_changes/test_beta_struct_changes.py +++ b/src/tests/beta/functional/struct_changes/test_beta_struct_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} diff --git a/src/tests/beta/functional/template_changes/__init__.py b/src/tests/beta/functional/template_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/template_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/type_def_changes/__init__.py b/src/tests/beta/functional/type_def_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/type_def_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/union_changes/__init__.py b/src/tests/beta/functional/union_changes/__init__.py new file mode 100644 index 0000000..21749a9 --- /dev/null +++ b/src/tests/beta/functional/union_changes/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/tests/beta/functional/union_changes/test_beta_union_changes.py b/src/tests/beta/functional/union_changes/test_beta_union_changes.py index 7742917..a68ad16 100644 --- a/src/tests/beta/functional/union_changes/test_beta_union_changes.py +++ b/src/tests/beta/functional/union_changes/test_beta_union_changes.py @@ -1,13 +1,12 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause - import os import json import subprocess from deepdiff import DeepDiff -def test_ast_diff(binary_path, binary_args, request): +def test_cpp_ast_diff(binary_path, binary_args, request): test_dir = os.path.dirname(request.fspath) @@ -28,3 +27,25 @@ def test_ast_diff(binary_path, binary_args, request): diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) assert diff == {} + +def test_c_ast_diff(binary_path, binary_args_c, request): + + test_dir = os.path.dirname(request.fspath) + + subprocess.run( + [binary_path] + binary_args_c, + check=True, + cwd=os.path.dirname(request.fspath) + ) + + print(test_dir) + + with open(f'{test_dir}/expected_output.json', 'r') as f: + expected_json = json.load(f) + + with open(f'{test_dir}/debug_output/ast_diffs/ast_diff_output_mylib.h.json', 'r') as f: + actual_json = json.load(f) + + diff = DeepDiff(expected_json, actual_json['astDiff'], ignore_order=True) + + assert diff == {} \ No newline at end of file diff --git a/src/tests/beta/src/options_handler.cpp b/src/tests/beta/src/options_handler.cpp index 40290ee..feb055d 100644 --- a/src/tests/beta/src/options_handler.cpp +++ b/src/tests/beta/src/options_handler.cpp @@ -1,6 +1,5 @@ // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. // SPDX-License-Identifier: BSD-3-Clause - #include #include #include @@ -16,6 +15,20 @@ #define TOOL_VERSION "" #endif +LANG_OPTIONS stringToLangOption(const std::string& lang) { + // Convert to lowercase for case-insensitive comparison + std::string lowerLang = lang; + std::transform(lowerLang.begin(), lowerLang.end(), lowerLang.begin(), + [](unsigned char c){ return std::tolower(c); }); + + if (lowerLang == LANG_C) { + return LANG_OPTIONS::C; + } else if (lowerLang == LANG_CPP) { + return LANG_OPTIONS::CPP; + } + return LANG_OPTIONS::CPP; // default to C++ +} + bool filesAreDifferentUsingDiff(const std::string &file1, const std::string &file2) { std::string command = "diff -q " + file1 + " " + file2 + " > /dev/null"; return std::system(command.c_str()) != 0; @@ -28,6 +41,7 @@ bool runArmorTool(int argc, const char **argv) { std::vector headers; std::string headerSubDir; std::string reportFormat = "html"; + std::string language = LANG_CPP; // default to C++ bool dumpAstDiff = false; std::string debugLevel = ""; std::vector IncludePaths; @@ -59,6 +73,9 @@ bool runArmorTool(int argc, const char **argv) { app.add_option("--report-format,-r", reportFormat, "Report format: html (default).\n" "If json is provided, both html and json reports will be generated.") ->check(CLI::IsMember({"html", "json"})); + app.add_option("--lang,-l", language, "Language mode: cpp (default) or c.\n" + "Use 'c' for C headers, 'cpp' for C++ headers.") + ->transform(CLI::IsMember({LANG_C, LANG_CPP}, CLI::ignore_case)); app.add_flag("--dump-ast-diff", dumpAstDiff, "Dump AST diff JSON files for debugging"); app.set_version_flag("--version,-v", TOOL_VERSION); app.add_option("--log-level", debugLevel, "Set debug log level: ERROR, LOG, INFO (default), DEBUG") @@ -97,6 +114,10 @@ bool runArmorTool(int argc, const char **argv) { debugConfig.setLevel(DebugConfig::Level::NONE); } + // Convert language string to LANG_OPTIONS enum + LANG_OPTIONS langOption = stringToLangOption(language); + armor::info() << "Language mode set to: " << language << "\n"; + bool processed = false; std::vector headersToCompare; if (!headers.empty()) { @@ -105,21 +126,26 @@ bool runArmorTool(int argc, const char **argv) { if (!headerSubDir.empty()) { file1 = projectRoot1 + "/" + headerSubDir + "/" + header; file2 = projectRoot2 + "/" + headerSubDir + "/" + header; - } else { + } + else { file1 = projectRoot1 + "/" + header; file2 = projectRoot2 + "/" + header; } armor::user_print() << "Processing files: " << file1 << " " << file2; if (!std::filesystem::exists(file1)) { armor::user_error() << "Missing header in older version: " << file1; - } else if (!std::filesystem::exists(file2)) { + } + else if (!std::filesystem::exists(file2)) { armor::user_error() << "Missing header in newer version: " << file2; - } else if (filesAreDifferentUsingDiff(file1, file2)) { + } + else if (filesAreDifferentUsingDiff(file1, file2)) { processHeaderPairBeta(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); + IncludePaths, macros, langOption); processed = true; - } else { + } + else { armor::user_print() << "No differences found between: " << file1 << " and " << file2; + return true; } } } @@ -141,14 +167,18 @@ bool runArmorTool(int argc, const char **argv) { armor::user_print() << "Processing files: " << file1 << " " << file2; if (!std::filesystem::exists(file1)) { armor::user_error() << "Missing header in older version: " << file1; - } else if (!std::filesystem::exists(file2)) { + } + else if (!std::filesystem::exists(file2)) { armor::user_error() << "Missing header in newer version: " << file2; - } else if (filesAreDifferentUsingDiff(file1, file2)) { + } + else if (filesAreDifferentUsingDiff(file1, file2)) { processHeaderPairBeta(projectRoot1, file1, projectRoot2, file2, reportFormat, - IncludePaths, macros); + IncludePaths, macros, langOption); processed = true; - } else { + } + else { armor::user_print() << "No differences found between: " << file1 << " and " << file2; + return true; } } } diff --git a/src/tests/common/unit/test_categorization.cpp b/src/tests/common/unit/test_categorization.cpp index a109b01..5730270 100644 --- a/src/tests/common/unit/test_categorization.cpp +++ b/src/tests/common/unit/test_categorization.cpp @@ -1,6 +1,5 @@ // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. // SPDX-License-Identifier: BSD-3-Clause - #include #include "categorization.hpp" #include "diff_utils.hpp" @@ -95,7 +94,7 @@ TEST_F(CategorizationTest, Changed_NonFunctionalChanges_Incompatible) { TEST_F(CategorizationTest, Unchanged_FatalErrors) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::FATAL_ERRORS), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); EXPECT_EQ(serialize(OverAllStatus::FATAL_ERRORS), result); @@ -104,7 +103,7 @@ TEST_F(CategorizationTest, Unchanged_FatalErrors) { TEST_F(CategorizationTest, Unchanged_UnsupportedUpdates_Compatible) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::UNSUPPORTED_UPDATES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), true ); EXPECT_EQ(serialize(OverAllStatus::UNSUPPORTED_UPDATES), result); @@ -113,7 +112,7 @@ TEST_F(CategorizationTest, Unchanged_UnsupportedUpdates_Compatible) { TEST_F(CategorizationTest, Unchanged_UnsupportedUpdates_Incompatible) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::UNSUPPORTED_UPDATES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); EXPECT_EQ(serialize(OverAllStatus::BACKWARD_INCOMPATIBLE), result); @@ -122,7 +121,7 @@ TEST_F(CategorizationTest, Unchanged_UnsupportedUpdates_Incompatible) { TEST_F(CategorizationTest, Unchanged_SupportedUpdates_Compatible) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), true ); EXPECT_EQ(serialize(OverAllStatus::BACKWARD_COMPATIABLE), result); @@ -131,7 +130,7 @@ TEST_F(CategorizationTest, Unchanged_SupportedUpdates_Compatible) { TEST_F(CategorizationTest, Unchanged_SupportedUpdates_Incompatible) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); EXPECT_EQ(serialize(OverAllStatus::BACKWARD_INCOMPATIBLE), result); @@ -140,7 +139,7 @@ TEST_F(CategorizationTest, Unchanged_SupportedUpdates_Incompatible) { TEST_F(CategorizationTest, Unchanged_CommentsUpdated) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::COMMENTS_UPDATED), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), true ); EXPECT_EQ(serialize(OverAllStatus::COMMENTS_UPDATED), result); @@ -149,7 +148,7 @@ TEST_F(CategorizationTest, Unchanged_CommentsUpdated) { TEST_F(CategorizationTest, Unchanged_NonFunctionalChanges) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::NON_FUNCTIONAL_CHANGES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), true ); EXPECT_EQ(serialize(OverAllStatus::NON_FUNCTIONAL_CHANGES), result); @@ -158,7 +157,7 @@ TEST_F(CategorizationTest, Unchanged_NonFunctionalChanges) { TEST_F(CategorizationTest, Unchanged_CommentsUpdated_Incompatible) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::COMMENTS_UPDATED), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); EXPECT_EQ(serialize(OverAllStatus::CATEGORIZATION_ERROR), result); @@ -167,7 +166,7 @@ TEST_F(CategorizationTest, Unchanged_CommentsUpdated_Incompatible) { TEST_F(CategorizationTest, Unchanged_NonFunctionalChanges_Incompatible) { std::string result = getOverAllCategory( static_cast(ParsedDiffStatus::NON_FUNCTIONAL_CHANGES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); EXPECT_EQ(serialize(OverAllStatus::CATEGORIZATION_ERROR), result); @@ -185,7 +184,7 @@ TEST_F(CategorizationTest, InvalidParsedStatus_Changed) { TEST_F(CategorizationTest, InvalidParsedStatus_Unchanged) { std::string result = getOverAllCategory( 999, - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); EXPECT_EQ(serialize(OverAllStatus::CATEGORIZATION_ERROR), result); diff --git a/src/tests/common/unit/test_categorization_reason.cpp b/src/tests/common/unit/test_categorization_reason.cpp index 505d971..16f3137 100644 --- a/src/tests/common/unit/test_categorization_reason.cpp +++ b/src/tests/common/unit/test_categorization_reason.cpp @@ -1,6 +1,5 @@ // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. // SPDX-License-Identifier: BSD-3-Clause - #include #include "categorization.hpp" #include "diff_utils.hpp" @@ -84,7 +83,7 @@ TEST_F(CategorizationReasonTest, Changed_NonFunctionalChanges) { TEST_F(CategorizationReasonTest, Unchanged_FatalErrors) { const char* result = getReasonForCategorization( static_cast(ParsedDiffStatus::FATAL_ERRORS), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); @@ -94,7 +93,7 @@ TEST_F(CategorizationReasonTest, Unchanged_FatalErrors) { TEST_F(CategorizationReasonTest, Unchanged_UnsupportedUpdates_Compatible) { const char* result = getReasonForCategorization( static_cast(ParsedDiffStatus::UNSUPPORTED_UPDATES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), true ); @@ -104,7 +103,7 @@ TEST_F(CategorizationReasonTest, Unchanged_UnsupportedUpdates_Compatible) { TEST_F(CategorizationReasonTest, Unchanged_UnsupportedUpdates_Incompatible) { const char* result = getReasonForCategorization( static_cast(ParsedDiffStatus::UNSUPPORTED_UPDATES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); @@ -114,7 +113,7 @@ TEST_F(CategorizationReasonTest, Unchanged_UnsupportedUpdates_Incompatible) { TEST_F(CategorizationReasonTest, Unchanged_SupportedUpdates_Compatible) { const char* result = getReasonForCategorization( static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), true ); @@ -124,7 +123,7 @@ TEST_F(CategorizationReasonTest, Unchanged_SupportedUpdates_Compatible) { TEST_F(CategorizationReasonTest, Unchanged_SupportedUpdates_Incompatible) { const char* result = getReasonForCategorization( static_cast(ParsedDiffStatus::SUPPORTED_UPDATES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); @@ -134,7 +133,7 @@ TEST_F(CategorizationReasonTest, Unchanged_SupportedUpdates_Incompatible) { TEST_F(CategorizationReasonTest, Unchanged_CommentsUpdated) { const char* result = getReasonForCategorization( static_cast(ParsedDiffStatus::COMMENTS_UPDATED), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); @@ -144,7 +143,7 @@ TEST_F(CategorizationReasonTest, Unchanged_CommentsUpdated) { TEST_F(CategorizationReasonTest, Unchanged_NonFunctionalChanges) { const char* result = getReasonForCategorization( static_cast(ParsedDiffStatus::NON_FUNCTIONAL_CHANGES), - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false ); @@ -164,7 +163,7 @@ TEST_F(CategorizationReasonTest, InvalidParsedStatus_Changed) { TEST_F(CategorizationReasonTest, InvalidParsedStatus_Unchanged) { const char* result = getReasonForCategorization( 999, - static_cast(UnParsedDiffStatus::UN_CHANGES), + static_cast(UnParsedDiffStatus::UN_CHANGED), false );