Blame tools/generate_cfg_tests.cpp

Packit 2035a7
Packit 2035a7
#include <iostream>
Packit 2035a7
#include <cstring>
Packit 2035a7
#include <string>
Packit 2035a7
#include <sstream>
Packit 2035a7
#include <vector>
Packit 2035a7
#include "tinyxml2.h"
Packit 2035a7
Packit 2035a7
static void testfunction(const tinyxml2::XMLElement *node, const std::string &functionName);
Packit 2035a7
Packit 2035a7
static std::vector<std::string> splitString(const char *str, char delim)
Packit 2035a7
{
Packit 2035a7
    std::vector<std::string> ret;
Packit 2035a7
    while (const char *p = std::strchr(str,delim)) {
Packit 2035a7
        ret.push_back(std::string(str, p-str));
Packit 2035a7
        str = p + 1;
Packit 2035a7
    }
Packit 2035a7
    ret.push_back(str);
Packit 2035a7
    return ret;
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
static std::string replaceCharInString(std::string s, char from, char to)
Packit 2035a7
{
Packit 2035a7
    for (int i = 0; i < s.size(); ++i) {
Packit 2035a7
        if (s[i] == from)
Packit 2035a7
            s[i] = to;
Packit 2035a7
    }
Packit 2035a7
    return s;
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
int main(int argc, char **argv)
Packit 2035a7
{
Packit 2035a7
    if (argc != 2 || !std::strstr(argv[1], ".cfg")) {
Packit 2035a7
        std::cerr << "no cfg file\n";
Packit 2035a7
        return 1;
Packit 2035a7
    }
Packit 2035a7
Packit 2035a7
    const char *cfgfile = argv[1];
Packit 2035a7
Packit 2035a7
    tinyxml2::XMLDocument doc;
Packit 2035a7
    if (tinyxml2::XML_SUCCESS != doc.LoadFile(cfgfile)) {
Packit 2035a7
        std::cerr << "failed to load cfg file\n";
Packit 2035a7
        return 1;
Packit 2035a7
    }
Packit 2035a7
Packit 2035a7
    std::string testfile(cfgfile);
Packit 2035a7
    if (testfile.find('/') != std::string::npos)
Packit 2035a7
        testfile = testfile.substr(testfile.rfind('/')+1);
Packit 2035a7
    testfile = "generated-cfg-tests-" + testfile.substr(0,testfile.find('.')) + ".cpp";
Packit 2035a7
Packit 2035a7
    std::cout << "// auto generated tests from " << cfgfile << std::endl;
Packit 2035a7
    std::cout << "//" << std::endl;
Packit 2035a7
    std::cout << "// Generated by command:" << std::endl;
Packit 2035a7
    std::cout << "// " << argv[0] << ' ' << cfgfile << " > " << testfile << std::endl;
Packit 2035a7
    std::cout << "//" << std::endl;
Packit 2035a7
    std::cout << "// Recommended cppcheck command line:" << std::endl;
Packit 2035a7
    std::cout << "// $ cppcheck --enable=warning,information --inline-suppr --platform=unix64 " << testfile << std::endl;
Packit 2035a7
    std::cout << "// => 'unmatched suppression' warnings are false negatives." << std::endl;
Packit 2035a7
    std::cout << "//" << std::endl << std::endl ;
Packit 2035a7
Packit 2035a7
    const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
Packit 2035a7
Packit 2035a7
    for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
Packit 2035a7
        const std::string nodename = node->Name();
Packit 2035a7
        if (nodename == "memory" || nodename == "resource") {
Packit 2035a7
            //testalloc(node);
Packit 2035a7
        } else if (nodename == "function") {
Packit 2035a7
            for (const std::string &name : splitString(node->Attribute("name"), ','))
Packit 2035a7
                testfunction(node, name);
Packit 2035a7
        }
Packit 2035a7
    }
Packit 2035a7
Packit 2035a7
    return 0;
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
static std::string functionCall(const std::string &functionName, bool useretval, int argNr, int numberOfArgs, const char badArg[])
Packit 2035a7
{
Packit 2035a7
    std::ostringstream ostr;
Packit 2035a7
    if (useretval)
Packit 2035a7
        ostr << "result = ";
Packit 2035a7
    ostr << functionName << '(';
Packit 2035a7
    for (int i = 1; i <= numberOfArgs; ++i) {
Packit 2035a7
        if (i > 1)
Packit 2035a7
            ostr << ", ";
Packit 2035a7
        if (i == argNr)
Packit 2035a7
            ostr << badArg;
Packit 2035a7
        else
Packit 2035a7
            ostr << "arg" << i;
Packit 2035a7
    }
Packit 2035a7
    ostr << ')';
Packit 2035a7
    return ostr.str();
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
static std::string testFunctionArg(const std::string &functionName, bool useretval, const char type[], int argNr, int numberOfArgs, const char code[], const char suppress[], const char badArg[])
Packit 2035a7
{
Packit 2035a7
    std::ostringstream ostr;
Packit 2035a7
    ostr << "void test__" << replaceCharInString(functionName,':','_') << "__arg" << argNr << "__" << type << "() {" << std::endl;
Packit 2035a7
    if (*code)
Packit 2035a7
        ostr << "  " << code << std::endl;
Packit 2035a7
    ostr << "  // cppcheck-suppress " << suppress << std::endl;
Packit 2035a7
    ostr << "  " << functionCall(functionName, useretval, argNr, numberOfArgs, badArg) << ';' << std::endl;
Packit 2035a7
    ostr << '}';
Packit 2035a7
    return ostr.str();
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
static void testfunction(const tinyxml2::XMLElement *node, const std::string &functionName)
Packit 2035a7
{
Packit 2035a7
    // How many args does this function take?
Packit 2035a7
    int numberOfArgs = 0;
Packit 2035a7
    for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
Packit 2035a7
        if (std::strcmp(child->Name(), "arg") == 0) {
Packit 2035a7
            const char *nr = child->Attribute("nr");
Packit 2035a7
            if (nr && std::isdigit(*nr) && std::atoi(nr) > numberOfArgs)
Packit 2035a7
                numberOfArgs = std::atoi(nr);
Packit 2035a7
        }
Packit 2035a7
    }
Packit 2035a7
Packit 2035a7
    bool noreturn = false;
Packit 2035a7
    bool useretval = false;
Packit 2035a7
    bool pure = false;
Packit 2035a7
    //bool constant = false;
Packit 2035a7
    bool leakignore = false;
Packit 2035a7
    for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
Packit 2035a7
        if (std::strcmp(child->Name(), "noreturn") == 0)
Packit 2035a7
            noreturn = true;
Packit 2035a7
        else if (std::strcmp(child->Name(), "use-retval") == 0)
Packit 2035a7
            useretval = true;
Packit 2035a7
        else if (std::strcmp(child->Name(), "pure") == 0)
Packit 2035a7
            pure = true;
Packit 2035a7
        //else if (std::strcmp(child->Name(), "const") == 0)
Packit 2035a7
        //    constant = true;
Packit 2035a7
        else if (std::strcmp(child->Name(), "leak-ignore") == 0)
Packit 2035a7
            leakignore = true;
Packit 2035a7
    }
Packit 2035a7
Packit 2035a7
    if (noreturn) {
Packit 2035a7
        std::cout << "void test__" << replaceCharInString(functionName,':','_') << "__noreturn() {" << std::endl;
Packit 2035a7
        std::cout << "  int x = 1;" << std::endl;
Packit 2035a7
        std::cout << "  if (cond) { x=100; " << functionCall(functionName, useretval, -1, numberOfArgs, "") << "; }" << std::endl;
Packit 2035a7
        std::cout << "  // cppcheck-suppress shiftTooManyBits" << std::endl;
Packit 2035a7
        std::cout << "  x = 1 << x;" << std::endl;
Packit 2035a7
        std::cout << "}" << std::endl << std::endl;
Packit 2035a7
    }
Packit 2035a7
    if (useretval) {
Packit 2035a7
        std::cout << "void test__" << replaceCharInString(functionName,':','_') << "__useretval() {" << std::endl;
Packit 2035a7
        std::cout << "  // cppcheck-suppress ignoredReturnValue" << std::endl;
Packit 2035a7
        std::cout << "  " << functionCall(functionName, false, -1, numberOfArgs, "") << ';' << std::endl;
Packit 2035a7
        std::cout << "}" << std::endl << std::endl;
Packit 2035a7
    }
Packit 2035a7
    if (pure) {
Packit 2035a7
        std::cout << "void test__" << replaceCharInString(functionName,':','_') << "__pure(";
Packit 2035a7
        for (int i = 1; i <= numberOfArgs; ++i)
Packit 2035a7
            std::cout << (i>1?",":"") << "int arg" << i;
Packit 2035a7
        std::cout << ") {" << std::endl;
Packit 2035a7
        std::cout << "  // cppcheck-suppress incorrectLogicOperator" << std::endl;
Packit 2035a7
        std::cout << "  if ((" << functionCall(functionName, false, -1, numberOfArgs, "") << " > 10) || (";
Packit 2035a7
        std::cout << functionCall(functionName, false, -1, numberOfArgs, "") <<  " < 100)) {}" << std::endl;
Packit 2035a7
        std::cout << "}" << std::endl << std::endl;
Packit 2035a7
    }
Packit 2035a7
    if (leakignore && functionName.find("::") == std::string::npos) {
Packit 2035a7
        std::cout << "void test__" << replaceCharInString(functionName,':','_') << "__leakignore() {" << std::endl;
Packit 2035a7
        std::cout << "  char *p = malloc(10); *p=0;" << std::endl;
Packit 2035a7
        std::cout << "  " << functionCall(functionName, useretval, 1, numberOfArgs, "p") << ';' << std::endl;
Packit 2035a7
        std::cout << "  // cppcheck-suppress memleak" << std::endl;
Packit 2035a7
        std::cout << "}" << std::endl << std::endl;
Packit 2035a7
    }
Packit 2035a7
Packit 2035a7
    // Generate tests for args
Packit 2035a7
    for (int argNr = 1; argNr <= numberOfArgs; ++argNr) {
Packit 2035a7
        for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
Packit 2035a7
            if (std::strcmp(child->Name(), "arg") != 0)
Packit 2035a7
                continue;
Packit 2035a7
            const char *nrAttr = child->Attribute("nr");
Packit 2035a7
            if (!nrAttr || std::atoi(nrAttr) != argNr)
Packit 2035a7
                continue;
Packit 2035a7
Packit 2035a7
            bool notbool = false;
Packit 2035a7
            bool notuninit = false;
Packit 2035a7
            bool notnull = false;
Packit 2035a7
Packit 2035a7
            for (const tinyxml2::XMLElement *argCheck = child->FirstChildElement(); argCheck; argCheck = argCheck->NextSiblingElement()) {
Packit 2035a7
                if (std::strcmp(argCheck->Name(), "not-bool") == 0)
Packit 2035a7
                    notbool = true;
Packit 2035a7
                else if (std::strcmp(argCheck->Name(), "not-null") == 0)
Packit 2035a7
                    notnull = true;
Packit 2035a7
                else if (std::strcmp(argCheck->Name(), "not-uninit") == 0)
Packit 2035a7
                    notuninit = true;
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            if (notbool) {
Packit 2035a7
                std::cout << testFunctionArg(functionName, useretval, "notbool", argNr, numberOfArgs, "", "invalidFunctionArgBool", "!x") << std::endl << std::endl;
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            if (notnull) {
Packit 2035a7
                std::cout << testFunctionArg(functionName, useretval, "notnull", argNr, numberOfArgs, "", "nullPointer", "NULL") << std::endl << std::endl;
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            if (notuninit) {
Packit 2035a7
                const char *code = notnull ? "int x[10];" : "int x;";
Packit 2035a7
                std::cout << testFunctionArg(functionName, useretval, "notuninit", argNr, numberOfArgs, code, "uninitvar", "x") << std::endl << std::endl;
Packit 2035a7
            }
Packit 2035a7
        }
Packit 2035a7
    }
Packit 2035a7
}