<commit_before>/** \file translation_db_tool.cc
 *  \brief A tool for reading/editing of the "translations" SQL table.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2016 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <set>
#include <cstdlib>
#include <cstring>
#include "Compiler.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "DbRow.h"
#include "IniFile.h"
#include "MiscUtil.h"
#include "SimpleXmlParser.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "TranslationUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " command [args]\n\n";
    std::cerr << "       Possible commands are:\n";
    std::cerr << "       get_missing language_code\n";
    std::cerr << "       insert token language_code text translator\n";
    std::cerr << "       insert ppn gnd_code language_code text translator\n";
    std::cerr << "       update token language_code text translator\n";
    std::cerr << "       update ppn gnd_code language_code text translator\n";
    std::exit(EXIT_FAILURE);
}

void ExecSqlOrDie(const std::string &select_statement, DbConnection * const connection) {
    if (unlikely(not connection->query(select_statement)))
        Error("SQL Statement failed: " + select_statement + " (" + connection->getLastErrorMessage() + ")");
}

// Replaces a comma with "\," and a slash with "\\".
std::string EscapeCommasAndBackslashes(const std::string &text) {
    std::string escaped_text;
    for (const auto ch : text) {
        if (unlikely(ch == ',' or ch == '\\'))
            escaped_text += '\\';
        escaped_text += ch;
    }

    return escaped_text;
}

unsigned GetMissing(DbConnection * const connection, const std::string &table_name,
                    const std::string &table_key_name, const std::string &category,
                    const std::string &language_code, const std::string &additional_condition = "")
{
    // Find a token/ppn where "language_code" is missing:
    ExecSqlOrDie("SELECT distinct " + table_key_name + " FROM " + table_name + " WHERE " + table_key_name +
                 " NOT IN (SELECT distinct " + table_key_name + " FROM " + table_name +
                 " WHERE language_code = \"" + language_code + "\") "
                 + (additional_condition.empty() ? "" : " AND (" + additional_condition + ")")
                 + " ORDER BY RAND();", connection);
    DbResultSet keys_result_set(connection->getLastResultSet());
    if (keys_result_set.empty())
        return 0;

    // Print the contents of all rows with the token from the last query on stdout:
    DbRow row(keys_result_set.getNextRow());
    const std::string matching_key(row[table_key_name]);
    ExecSqlOrDie("SELECT * FROM " + table_name + " WHERE " + table_key_name + "='" + matching_key + "';", connection);
    DbResultSet result_set(connection->getLastResultSet());
    if (result_set.empty())
        return 0;

    const std::set<std::string> column_names(SqlUtil::GetColumnNames(connection, table_name));
    const bool has_gnd_code(column_names.find("gnd_code") != column_names.cend());

    while (row = result_set.getNextRow())
        std::cout << EscapeCommasAndBackslashes(row[table_key_name]) << ',' << keys_result_set.size() << ',' << row["language_code"] << ','
                  << EscapeCommasAndBackslashes(row["translation"]) << ',' << category
                  << (has_gnd_code ? "," + row["gnd_code"] : "") << '\n';

    return result_set.size();
}

unsigned GetMissingVuFindTranslations(DbConnection * const connection, const std::string &language_code) {
    return GetMissing(connection, "vufind_translations", "token", "vufind_translations", language_code);
}

unsigned GetMissingKeywordTranslations(DbConnection * const connection, const std::string &language_code) {
    return GetMissing(connection, "keyword_translations", "ppn", "keyword_translations", language_code, "status != \"reliable_synonym\" AND status != \"unreliable_synonym\"");
}

unsigned GetExisting(DbConnection * const connection, const std::string &table_name,
                    const std::string &table_key_name, const std::string &category,
                    const std::string &language_code, const std::string &index_value)
{
    // Find a token/ppn where "language_code" is missing:
    ExecSqlOrDie("SELECT distinct " + table_key_name + " FROM " + table_name + " WHERE " + table_key_name +
                 " NOT IN (SELECT distinct " + table_key_name + " FROM " + table_name +
                 " WHERE language_code = \"" + language_code + "\") ORDER BY RAND();", connection);
    DbResultSet keys_result_set(connection->getLastResultSet());
    const size_t count(keys_result_set.size());

    ExecSqlOrDie("SELECT * FROM " + table_name + " WHERE " + table_key_name + "='" + index_value + "';", connection);
    DbResultSet result_set(connection->getLastResultSet());
    if (result_set.empty())
        return 0;

    const std::set<std::string> column_names(SqlUtil::GetColumnNames(connection, table_name));
    const bool has_gnd_code(column_names.find("gnd_code") != column_names.cend());

    while (const DbRow row = result_set.getNextRow())
        std::cout << EscapeCommasAndBackslashes(row[table_key_name]) << ',' << count << ',' << row["language_code"] << ','
                  << EscapeCommasAndBackslashes(row["translation"]) << ',' << category
                  << (has_gnd_code ? "," + row["gnd_code"] : "") << '\n';

    return result_set.size();
}

unsigned GetExistingVuFindTranslations(DbConnection * const connection, const std::string &language_code, const std::string &index_value) {
    return GetExisting(connection, "vufind_translations", "token", "vufind_translations", language_code, index_value);
}

unsigned GetExistingKeywordTranslations(DbConnection * const connection, const std::string &language_code, const std::string &index_value) {
    return GetExisting(connection, "keyword_translations", "ppn", "keyword_translations", language_code, index_value);
}

void InsertIntoVuFindTranslations(DbConnection * const connection, const std::string &token,
                                  const std::string &language_code, const std::string &text, const std::string &translator)
{
    ExecSqlOrDie("INSERT INTO vufind_translations SET token=\"" + token + "\",language_code=\"" + language_code
                 + "\",translation=\"" + connection->escapeString(text) + "\",translator=\"" + translator + "\";", connection);
}

void InsertIntoKeywordTranslations(DbConnection * const connection, const std::string &ppn,
                                   const std::string &gnd_code, const std::string &language_code,
                                   const std::string &text, const std::string &translator)
{
    ExecSqlOrDie("INSERT INTO keyword_translations SET ppn=\"" + ppn + "\",gnd_code=\"" + gnd_code
                 + "\",language_code=\"" + language_code + "\",translation=\""
                 + connection->escapeString(text) + "\",origin=\"150\",status=\"new\"" + ",translator=\"" + translator + "\";", connection);
}

void UpdateIntoVuFindTranslations(DbConnection * const connection, const std::string &token,
                                  const std::string &language_code, const std::string &text, const std::string &translator)
{
    ExecSqlOrDie("UPDATE vufind_translations SET translation=\"" + connection->escapeString(text) + "\", translator=\"" + translator
                 + "\" WHERE token=\"" + token + "\" AND language_code=\"" + language_code
                 + "\";", connection);
}

void UpdateIntoKeywordTranslations(DbConnection * const connection, const std::string &ppn,
                                   const std::string &gnd_code, const std::string &language_code,
                                   const std::string &text, const std::string &translator)
{
    ExecSqlOrDie("UPDATE keyword_translations SET translation=\""
                 + connection->escapeString(text)
                 + "\", translator=\"" + translator
                 + "\" WHERE ppn=\"" + ppn + "\" AND gnd_code=\"" + gnd_code
                 + "\" AND language_code=\"" + language_code + "\";", connection);
}

void ValidateKeywordTranslation(DbConnection * const connection, const std::string &ppn, const std::string &translation) {
    const std::string query("SELECT translation FROM keyword_translations WHERE ppn = \"" + ppn + "\";");
    ExecSqlOrDie(query, connection);
    DbResultSet result_set(connection->getLastResultSet());

    while (const DbRow row = result_set.getNextRow()) {
        if (row["translation"].find("<") < row["translation"].find(">")
            and not(translation.find("<") < translation.find(">"))) {
            std::cout << "Your translation has to have a tag enclosed by '<' and '>'!";
            return;
        }
    }
}

const std::string CONF_FILE_PATH("/var/lib/tuelib/translations.conf");

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    try {
        if (argc < 2)
            Usage();

        const IniFile ini_file(CONF_FILE_PATH);
        const std::string sql_database(ini_file.getString("Database", "sql_database"));
        const std::string sql_username(ini_file.getString("Database", "sql_username"));
        const std::string sql_password(ini_file.getString("Database", "sql_password"));
        DbConnection db_connection(sql_database, sql_username, sql_password);

        if (std::strcmp(argv[1], "get_missing") == 0) {
            if (argc != 3)
                Error("\"get_missing\" requires exactly one argument: language_code!");
            const std::string language_code(argv[2]);
            if (not TranslationUtil::IsValidFake3LetterEnglishLanguagesCode(language_code))
                Error("\"" + language_code + "\" is not a valid fake 3-letter english language code!");
            if (not GetMissingVuFindTranslations(&db_connection, language_code))
                GetMissingKeywordTranslations(&db_connection, language_code);
        } else if (std::strcmp(argv[1], "get_existing") == 0) {
            if (argc != 5)
                Error("\"get_existing\" requires exactly three arguments: language_code category index!");
            const std::string language_code(argv[2]);
            if (not TranslationUtil::IsValidFake3LetterEnglishLanguagesCode(language_code))
                Error("\"" + language_code + "\" is not a valid fake 3-letter english language code!");
            const std::string category(argv[3]);
            const std::string index_value(argv[4]);
            if (category == "vufind_translations")
                GetExistingVuFindTranslations(&db_connection, language_code, index_value);
            else
                GetExistingKeywordTranslations(&db_connection, language_code, index_value);
        } else if (std::strcmp(argv[1], "insert") == 0) {
            if (argc != 6 and argc != 7)
                Error("\"insert\" requires four or five arguments: token or ppn, gnd_code (if ppn), "
                              "language_code, text, and translator!");

            const std::string language_code(argv[(argc == 6) ? 3 : 4]);
            if (not TranslationUtil::IsValidFake3LetterEnglishLanguagesCode(language_code))
                Error("\"" + language_code + "\" is not a valid fake 3-letter english language code!");

            if (argc == 6)
                InsertIntoVuFindTranslations(&db_connection, argv[2], language_code, argv[4], argv[5]);
            else
                InsertIntoKeywordTranslations(&db_connection, argv[2], argv[3], language_code, argv[5], argv[6]);
        } else if (std::strcmp(argv[1], "update") == 0) {
            if (argc != 6 and argc != 7)
                Error("\"update\" requires four or five arguments: token or ppn, gnd_code (if ppn), "
                      "language_code, text and translator!");

            const std::string language_code(argv[(argc == 6) ? 3 : 4]);
            if (not TranslationUtil::IsValidFake3LetterEnglishLanguagesCode(language_code))
                Error("\"" + language_code + "\" is not a valid fake 3-letter english language code!");

            if (argc == 6)
                UpdateIntoVuFindTranslations(&db_connection, argv[2], language_code, argv[4], argv[5]);
            else
                UpdateIntoKeywordTranslations(&db_connection, argv[2], argv[3], language_code, argv[5], argv[6]);
        } else if (std::strcmp(argv[1], "validate_keyword") == 0) {
            if (argc != 4)
                Error("\"get_missing\" requires exactly two argument: ppn translation!");
            const std::string ppn(argv[2]);
            const std::string translation(argv[3]);
            ValidateKeywordTranslation(&db_connection, ppn, translation);
        } else
            Error("unknown command \"" + std::string(argv[1]) + "\"!");
    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()) + " (login is " + MiscUtil::GetUserName() + ")");
    }
}
<commit_msg>Workaround to prevent MACS translations from being overwritten even on iterative writes<commit_after>/** \file translation_db_tool.cc
 *  \brief A tool for reading/editing of the "translations" SQL table.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2016 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <set>
#include <cstdlib>
#include <cstring>
#include "Compiler.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "DbRow.h"
#include "IniFile.h"
#include "MiscUtil.h"
#include "SimpleXmlParser.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "TranslationUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " command [args]\n\n";
    std::cerr << "       Possible commands are:\n";
    std::cerr << "       get_missing language_code\n";
    std::cerr << "       insert token language_code text translator\n";
    std::cerr << "       insert ppn gnd_code language_code text translator\n";
    std::cerr << "       update token language_code text translator\n";
    std::cerr << "       update ppn gnd_code language_code text translator\n";
    std::exit(EXIT_FAILURE);
}

void ExecSqlOrDie(const std::string &select_statement, DbConnection * const connection) {
    if (unlikely(not connection->query(select_statement)))
        Error("SQL Statement failed: " + select_statement + " (" + connection->getLastErrorMessage() + ")");
}

// Replaces a comma with "\," and a slash with "\\".
std::string EscapeCommasAndBackslashes(const std::string &text) {
    std::string escaped_text;
    for (const auto ch : text) {
        if (unlikely(ch == ',' or ch == '\\'))
            escaped_text += '\\';
        escaped_text += ch;
    }

    return escaped_text;
}

unsigned GetMissing(DbConnection * const connection, const std::string &table_name,
                    const std::string &table_key_name, const std::string &category,
                    const std::string &language_code, const std::string &additional_condition = "")
{
    // Find a token/ppn where "language_code" is missing:
    ExecSqlOrDie("SELECT distinct " + table_key_name + " FROM " + table_name + " WHERE " + table_key_name +
                 " NOT IN (SELECT distinct " + table_key_name + " FROM " + table_name +
                 " WHERE language_code = \"" + language_code + "\") "
                 + (additional_condition.empty() ? "" : " AND (" + additional_condition + ")")
                 + " ORDER BY RAND();", connection);
    DbResultSet keys_result_set(connection->getLastResultSet());
    if (keys_result_set.empty())
        return 0;

    // Print the contents of all rows with the token from the last query on stdout:
    DbRow row(keys_result_set.getNextRow());
    const std::string matching_key(row[table_key_name]);
    ExecSqlOrDie("SELECT * FROM " + table_name + " WHERE " + table_key_name + "='" + matching_key + "';", connection);
    DbResultSet result_set(connection->getLastResultSet());
    if (result_set.empty())
        return 0;

    const std::set<std::string> column_names(SqlUtil::GetColumnNames(connection, table_name));
    const bool has_gnd_code(column_names.find("gnd_code") != column_names.cend());

    while (row = result_set.getNextRow())
        std::cout << EscapeCommasAndBackslashes(row[table_key_name]) << ',' << keys_result_set.size() << ',' << row["language_code"] << ','
                  << EscapeCommasAndBackslashes(row["translation"]) << ',' << category
                  << (has_gnd_code ? "," + row["gnd_code"] : "") << '\n';

    return result_set.size();
}

unsigned GetMissingVuFindTranslations(DbConnection * const connection, const std::string &language_code) {
    return GetMissing(connection, "vufind_translations", "token", "vufind_translations", language_code);
}

unsigned GetMissingKeywordTranslations(DbConnection * const connection, const std::string &language_code) {
    return GetMissing(connection, "keyword_translations", "ppn", "keyword_translations", language_code, "status != \"reliable_synonym\" AND status != \"unreliable_synonym\"");
}

unsigned GetExisting(DbConnection * const connection, const std::string &table_name,
                    const std::string &table_key_name, const std::string &category,
                    const std::string &language_code, const std::string &index_value)
{
    // Find a token/ppn where "language_code" is missing:
    ExecSqlOrDie("SELECT distinct " + table_key_name + " FROM " + table_name + " WHERE " + table_key_name +
                 " NOT IN (SELECT distinct " + table_key_name + " FROM " + table_name +
                 " WHERE language_code = \"" + language_code + "\") ORDER BY RAND();", connection);
    DbResultSet keys_result_set(connection->getLastResultSet());
    const size_t count(keys_result_set.size());

    ExecSqlOrDie("SELECT * FROM " + table_name + " WHERE " + table_key_name + "='" + index_value + "';", connection);
    DbResultSet result_set(connection->getLastResultSet());
    if (result_set.empty())
        return 0;

    const std::set<std::string> column_names(SqlUtil::GetColumnNames(connection, table_name));
    const bool has_gnd_code(column_names.find("gnd_code") != column_names.cend());

    while (const DbRow row = result_set.getNextRow())
        std::cout << EscapeCommasAndBackslashes(row[table_key_name]) << ',' << count << ',' << row["language_code"] << ','
                  << EscapeCommasAndBackslashes(row["translation"]) << ',' << category
                  << (has_gnd_code ? "," + row["gnd_code"] : "") << '\n';

    return result_set.size();
}

unsigned GetExistingVuFindTranslations(DbConnection * const connection, const std::string &language_code, const std::string &index_value) {
    return GetExisting(connection, "vufind_translations", "token", "vufind_translations", language_code, index_value);
}

unsigned GetExistingKeywordTranslations(DbConnection * const connection, const std::string &language_code, const std::string &index_value) {
    return GetExisting(connection, "keyword_translations", "ppn", "keyword_translations", language_code, index_value);
}

void InsertIntoVuFindTranslations(DbConnection * const connection, const std::string &token,
                                  const std::string &language_code, const std::string &text, const std::string &translator)
{
    ExecSqlOrDie("INSERT INTO vufind_translations SET token=\"" + token + "\",language_code=\"" + language_code
                 + "\",translation=\"" + connection->escapeString(text) + "\",translator=\"" + translator + "\";", connection);
}

void InsertIntoKeywordTranslations(DbConnection * const connection, const std::string &ppn,
                                   const std::string &gnd_code, const std::string &language_code,
                                   const std::string &text, const std::string &translator)
{
    ExecSqlOrDie("INSERT INTO keyword_translations SET ppn=\"" + ppn + "\",gnd_code=\"" + gnd_code
                 + "\",language_code=\"" + language_code + "\",translation=\""
                 + connection->escapeString(text) + "\",origin=\"150\",status=\"new\"" + ",translator=\"" + translator + "\";", connection);
}

void UpdateIntoVuFindTranslations(DbConnection * const connection, const std::string &token,
                                  const std::string &language_code, const std::string &text, const std::string &translator)
{
    ExecSqlOrDie("UPDATE vufind_translations SET translation=\"" + connection->escapeString(text) + "\", translator=\"" + translator
                 + "\" WHERE token=\"" + token + "\" AND language_code=\"" + language_code
                 + "\";", connection);
}

void UpdateIntoKeywordTranslations(DbConnection * const connection, const std::string &ppn,
                                   const std::string &gnd_code, const std::string &language_code,
                                   const std::string &text, const std::string &translator)
{
    ExecSqlOrDie("UPDATE keyword_translations SET translation=\""
                 + connection->escapeString(text)
                 + "\", translator=\"" + translator
                 + "\" WHERE ppn=\"" + ppn + "\" AND gnd_code=\"" + gnd_code
                 + "\" AND language_code=\"" + language_code + "\"" + "AND status != \"unreliable\";", connection);
}

void ValidateKeywordTranslation(DbConnection * const connection, const std::string &ppn, const std::string &translation) {
    const std::string query("SELECT translation FROM keyword_translations WHERE ppn = \"" + ppn + "\";");
    ExecSqlOrDie(query, connection);
    DbResultSet result_set(connection->getLastResultSet());

    while (const DbRow row = result_set.getNextRow()) {
        if (row["translation"].find("<") < row["translation"].find(">")
            and not(translation.find("<") < translation.find(">"))) {
            std::cout << "Your translation has to have a tag enclosed by '<' and '>'!";
            return;
        }
    }
}

const std::string CONF_FILE_PATH("/var/lib/tuelib/translations.conf");

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    try {
        if (argc < 2)
            Usage();

        const IniFile ini_file(CONF_FILE_PATH);
        const std::string sql_database(ini_file.getString("Database", "sql_database"));
        const std::string sql_username(ini_file.getString("Database", "sql_username"));
        const std::string sql_password(ini_file.getString("Database", "sql_password"));
        DbConnection db_connection(sql_database, sql_username, sql_password);

        if (std::strcmp(argv[1], "get_missing") == 0) {
            if (argc != 3)
                Error("\"get_missing\" requires exactly one argument: language_code!");
            const std::string language_code(argv[2]);
            if (not TranslationUtil::IsValidFake3LetterEnglishLanguagesCode(language_code))
                Error("\"" + language_code + "\" is not a valid fake 3-letter english language code!");
            if (not GetMissingVuFindTranslations(&db_connection, language_code))
                GetMissingKeywordTranslations(&db_connection, language_code);
        } else if (std::strcmp(argv[1], "get_existing") == 0) {
            if (argc != 5)
                Error("\"get_existing\" requires exactly three arguments: language_code category index!");
            const std::string language_code(argv[2]);
            if (not TranslationUtil::IsValidFake3LetterEnglishLanguagesCode(language_code))
                Error("\"" + language_code + "\" is not a valid fake 3-letter english language code!");
            const std::string category(argv[3]);
            const std::string index_value(argv[4]);
            if (category == "vufind_translations")
                GetExistingVuFindTranslations(&db_connection, language_code, index_value);
            else
                GetExistingKeywordTranslations(&db_connection, language_code, index_value);
        } else if (std::strcmp(argv[1], "insert") == 0) {
            if (argc != 6 and argc != 7)
                Error("\"insert\" requires four or five arguments: token or ppn, gnd_code (if ppn), "
                              "language_code, text, and translator!");

            const std::string language_code(argv[(argc == 6) ? 3 : 4]);
            if (not TranslationUtil::IsValidFake3LetterEnglishLanguagesCode(language_code))
                Error("\"" + language_code + "\" is not a valid fake 3-letter english language code!");

            if (argc == 6)
                InsertIntoVuFindTranslations(&db_connection, argv[2], language_code, argv[4], argv[5]);
            else
                InsertIntoKeywordTranslations(&db_connection, argv[2], argv[3], language_code, argv[5], argv[6]);
        } else if (std::strcmp(argv[1], "update") == 0) {
            if (argc != 6 and argc != 7)
                Error("\"update\" requires four or five arguments: token or ppn, gnd_code (if ppn), "
                      "language_code, text and translator!");

            const std::string language_code(argv[(argc == 6) ? 3 : 4]);
            if (not TranslationUtil::IsValidFake3LetterEnglishLanguagesCode(language_code))
                Error("\"" + language_code + "\" is not a valid fake 3-letter english language code!");

            if (argc == 6)
                UpdateIntoVuFindTranslations(&db_connection, argv[2], language_code, argv[4], argv[5]);
            else
                UpdateIntoKeywordTranslations(&db_connection, argv[2], argv[3], language_code, argv[5], argv[6]);
        } else if (std::strcmp(argv[1], "validate_keyword") == 0) {
            if (argc != 4)
                Error("\"get_missing\" requires exactly two argument: ppn translation!");
            const std::string ppn(argv[2]);
            const std::string translation(argv[3]);
            ValidateKeywordTranslation(&db_connection, ppn, translation);
        } else
            Error("unknown command \"" + std::string(argv[1]) + "\"!");
    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()) + " (login is " + MiscUtil::GetUserName() + ")");
    }
}
<|endoftext|>


<commit_before>//===--- CacheTest.cpp ----------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/Basic/Cache.h"
#include "gtest/gtest.h"

#if defined(__APPLE__)
#define USES_LIBCACHE 1
#else
#define USES_LIBCACHE 0
#endif

namespace {
struct Counter {
  mutable int enter = 0;
  mutable int exit = 0;
};
}

namespace swift {
namespace sys {
template <>
struct CacheValueInfo<Counter>{
  static void *enterCache(const Counter &value) {
    return const_cast<Counter *>(&value);
  }
  static void retain(void *ptr) {
    static_cast<Counter*>(ptr)->enter+= 1;
  }
  static void release(void *ptr) {
    static_cast<Counter*>(ptr)->exit += 1;
  }
  static const Counter &getFromCache(void *ptr) {
    return *static_cast<Counter *>(ptr);
  }
  static size_t getCost(const Counter &value) {
    return 0;
  }
};
}
}

namespace {
struct KeyCounter {
  int key = 0;
  mutable int enter = 0;
  mutable int exit = 0;
};
}

namespace swift {
namespace sys {
template <>
struct CacheKeyInfo<KeyCounter> {
  static uintptr_t getHashValue(const KeyCounter &value) {
    return llvm::DenseMapInfo<int>::getHashValue(value.key);
  }
  static bool isEqual(void *lhs, void *rhs) {
    return static_cast<KeyCounter *>(lhs)->key ==
        static_cast<KeyCounter *>(rhs)->key;
  }
  static void *enterCache(const KeyCounter &value) {
    value.enter += 1;
    return const_cast<KeyCounter *>(&value);
  }
  static void exitCache(void *value) {
    static_cast<KeyCounter *>(value)->exit += 1;
  }
  static const void *getLookupKey(const KeyCounter *value) {
    return value;
  }
  static const KeyCounter &getFromCache(void *value) {
    return *static_cast<KeyCounter *>(value);
  }
};
}
}

namespace {
struct RefCntToken : llvm::RefCountedBase<RefCntToken> {
  bool &freed;
  RefCntToken(bool &freed) : freed(freed) {}
  ~RefCntToken() { freed = true; }
};
}

TEST(Cache, sameKey) {
  Counter c1, c2;
  swift::sys::Cache<const char *, Counter> cache(__func__);
  cache.set("a", c1);
  EXPECT_EQ(1, c1.enter);
  EXPECT_EQ(0, c1.exit);

  cache.set("a", c2);
  EXPECT_EQ(1, c1.enter);
  EXPECT_EQ(1, c1.exit);
  EXPECT_EQ(1, c2.enter);
  EXPECT_EQ(0, c2.exit);
}

TEST(Cache, sameValue) {
  Counter c;
  swift::sys::Cache<const char *, Counter> cache(__func__);
  cache.set("a", c);
  EXPECT_EQ(1, c.enter);
  EXPECT_EQ(0, c.exit);

  cache.set("b", c);
#if USES_LIBCACHE
  EXPECT_EQ(1, c.enter); // value is shared.
#else
  EXPECT_EQ(2, c.enter);
#endif
  EXPECT_EQ(0, c.exit);

  cache.remove("a");
#if USES_LIBCACHE
  EXPECT_EQ(1, c.enter); // value is shared.
  EXPECT_EQ(0, c.exit);
#else
  EXPECT_EQ(2, c.enter);
  EXPECT_EQ(1, c.exit);
#endif

  cache.remove("b");
  EXPECT_EQ(c.enter, c.exit);
}

TEST(Cache, sameKeyValue) {
  Counter c;
  swift::sys::Cache<const char *, Counter> cache(__func__);
  cache.set("a", c);
  EXPECT_EQ(1, c.enter);
  EXPECT_EQ(0, c.exit);

  cache.set("a", c);
  EXPECT_EQ(1, c.enter);
  EXPECT_EQ(0, c.exit);

  cache.remove("a");
  EXPECT_EQ(1, c.enter);
  EXPECT_EQ(1, c.exit);
}

TEST(Cache, sameKeyValueDestroysKey) {
  swift::sys::Cache<KeyCounter, Counter> cache(__func__);
  KeyCounter k1, k2;
  Counter c;
  cache.set(k1, c);
  cache.set(k2, c);
  EXPECT_EQ(1, k1.enter);
  EXPECT_EQ(1, k1.exit);
  EXPECT_EQ(1, k2.enter);
  EXPECT_EQ(0, k2.exit);
}

TEST(Cache, sameKeyIntrusiveRefCountPter) {
  bool freed1 = false;
  bool freed2 = false;
  swift::sys::Cache<const char *, llvm::IntrusiveRefCntPtr<RefCntToken>> cache(__func__);
  {
    llvm::IntrusiveRefCntPtr<RefCntToken> c1(new RefCntToken(freed1));
    llvm::IntrusiveRefCntPtr<RefCntToken> c2(new RefCntToken(freed2));
    cache.set("a", c1);
    cache.set("a", c2);
  }
  EXPECT_TRUE(freed1);
  EXPECT_FALSE(freed2);
  cache.remove("a");
  EXPECT_TRUE(freed2);
}

TEST(Cache, sameValueIntrusiveRefCountPter) {
  bool freed = false;
  swift::sys::Cache<const char *, llvm::IntrusiveRefCntPtr<RefCntToken>> cache(__func__);
  {
    llvm::IntrusiveRefCntPtr<RefCntToken> c(new RefCntToken(freed));
    cache.set("a", c);
    EXPECT_FALSE(freed);

    cache.set("b", c);
    EXPECT_FALSE(freed);

    cache.remove("a");
    EXPECT_FALSE(freed);

    cache.remove("b");
    EXPECT_FALSE(freed);
  }
  EXPECT_TRUE(freed);
}

TEST(Cache, sameKeyValueIntrusiveRefCountPter) {
  bool freed = false;
  swift::sys::Cache<const char *, llvm::IntrusiveRefCntPtr<RefCntToken>> cache(__func__);
  {
    llvm::IntrusiveRefCntPtr<RefCntToken> c(new RefCntToken(freed));
    cache.set("a", c);
    EXPECT_FALSE(freed);
    cache.set("a", c);
    EXPECT_FALSE(freed);
  }
  EXPECT_FALSE(freed);
  cache.remove("a");
  EXPECT_TRUE(freed);
}

TEST(Cache, copyValue) {
  struct S {
    int ident, copy;
    S(int ident) : ident(ident), copy(0) {}
    S(const S &other) : ident(other.ident), copy(other.copy+1) {}
    S(S &&other) : ident(other.ident), copy(other.copy) {}
  };
  swift::sys::Cache<const char *, struct S> cache(__func__);
  S s{0};
  EXPECT_EQ(0, s.ident);
  EXPECT_EQ(0, s.copy);
  cache.set("a", s);
  EXPECT_EQ(0, cache.get("a")->ident);
  EXPECT_EQ(2, cache.get("a")->copy); // return by value causes 2nd copy
  cache.set("b", *cache.get("a"));
  EXPECT_EQ(0, cache.get("b")->ident);
  EXPECT_EQ(4, cache.get("b")->copy); // return by value causes 2nd copy
}
<commit_msg>fix ASAN in cache test<commit_after>//===--- CacheTest.cpp ----------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/Basic/Cache.h"
#include "gtest/gtest.h"

#if defined(__APPLE__)
#define USES_LIBCACHE 1
#else
#define USES_LIBCACHE 0
#endif

namespace {
struct Counter {
  mutable int enter = 0;
  mutable int exit = 0;
};
}

namespace swift {
namespace sys {
template <>
struct CacheValueInfo<Counter>{
  static void *enterCache(const Counter &value) {
    return const_cast<Counter *>(&value);
  }
  static void retain(void *ptr) {
    static_cast<Counter*>(ptr)->enter+= 1;
  }
  static void release(void *ptr) {
    static_cast<Counter*>(ptr)->exit += 1;
  }
  static const Counter &getFromCache(void *ptr) {
    return *static_cast<Counter *>(ptr);
  }
  static size_t getCost(const Counter &value) {
    return 0;
  }
};
}
}

namespace {
struct KeyCounter {
  int key = 0;
  mutable int enter = 0;
  mutable int exit = 0;
};
}

namespace swift {
namespace sys {
template <>
struct CacheKeyInfo<KeyCounter> {
  static uintptr_t getHashValue(const KeyCounter &value) {
    return llvm::DenseMapInfo<int>::getHashValue(value.key);
  }
  static bool isEqual(void *lhs, void *rhs) {
    return static_cast<KeyCounter *>(lhs)->key ==
        static_cast<KeyCounter *>(rhs)->key;
  }
  static void *enterCache(const KeyCounter &value) {
    value.enter += 1;
    return const_cast<KeyCounter *>(&value);
  }
  static void exitCache(void *value) {
    static_cast<KeyCounter *>(value)->exit += 1;
  }
  static const void *getLookupKey(const KeyCounter *value) {
    return value;
  }
  static const KeyCounter &getFromCache(void *value) {
    return *static_cast<KeyCounter *>(value);
  }
};
}
}

namespace {
struct RefCntToken : llvm::RefCountedBase<RefCntToken> {
  bool &freed;
  RefCntToken(bool &freed) : freed(freed) {}
  ~RefCntToken() { freed = true; }
};
}

TEST(Cache, sameKey) {
  Counter c1, c2;
  swift::sys::Cache<const char *, Counter> cache(__func__);
  cache.set("a", c1);
  EXPECT_EQ(1, c1.enter);
  EXPECT_EQ(0, c1.exit);

  cache.set("a", c2);
  EXPECT_EQ(1, c1.enter);
  EXPECT_EQ(1, c1.exit);
  EXPECT_EQ(1, c2.enter);
  EXPECT_EQ(0, c2.exit);
}

TEST(Cache, sameValue) {
  Counter c;
  swift::sys::Cache<const char *, Counter> cache(__func__);
  cache.set("a", c);
  EXPECT_EQ(1, c.enter);
  EXPECT_EQ(0, c.exit);

  cache.set("b", c);
#if USES_LIBCACHE
  EXPECT_EQ(1, c.enter); // value is shared.
#else
  EXPECT_EQ(2, c.enter);
#endif
  EXPECT_EQ(0, c.exit);

  cache.remove("a");
#if USES_LIBCACHE
  EXPECT_EQ(1, c.enter); // value is shared.
  EXPECT_EQ(0, c.exit);
#else
  EXPECT_EQ(2, c.enter);
  EXPECT_EQ(1, c.exit);
#endif

  cache.remove("b");
  EXPECT_EQ(c.enter, c.exit);
}

TEST(Cache, sameKeyValue) {
  Counter c;
  swift::sys::Cache<const char *, Counter> cache(__func__);
  cache.set("a", c);
  EXPECT_EQ(1, c.enter);
  EXPECT_EQ(0, c.exit);

  cache.set("a", c);
  EXPECT_EQ(1, c.enter);
  EXPECT_EQ(0, c.exit);

  cache.remove("a");
  EXPECT_EQ(1, c.enter);
  EXPECT_EQ(1, c.exit);
}

TEST(Cache, sameKeyValueDestroysKey) {
  KeyCounter k1, k2;
  Counter c;
  swift::sys::Cache<KeyCounter, Counter> cache(__func__);
  cache.set(k1, c);
  cache.set(k2, c);
  EXPECT_EQ(1, k1.enter);
  EXPECT_EQ(1, k1.exit);
  EXPECT_EQ(1, k2.enter);
  EXPECT_EQ(0, k2.exit);
}

TEST(Cache, sameKeyIntrusiveRefCountPter) {
  bool freed1 = false;
  bool freed2 = false;
  swift::sys::Cache<const char *, llvm::IntrusiveRefCntPtr<RefCntToken>> cache(__func__);
  {
    llvm::IntrusiveRefCntPtr<RefCntToken> c1(new RefCntToken(freed1));
    llvm::IntrusiveRefCntPtr<RefCntToken> c2(new RefCntToken(freed2));
    cache.set("a", c1);
    cache.set("a", c2);
  }
  EXPECT_TRUE(freed1);
  EXPECT_FALSE(freed2);
  cache.remove("a");
  EXPECT_TRUE(freed2);
}

TEST(Cache, sameValueIntrusiveRefCountPter) {
  bool freed = false;
  swift::sys::Cache<const char *, llvm::IntrusiveRefCntPtr<RefCntToken>> cache(__func__);
  {
    llvm::IntrusiveRefCntPtr<RefCntToken> c(new RefCntToken(freed));
    cache.set("a", c);
    EXPECT_FALSE(freed);

    cache.set("b", c);
    EXPECT_FALSE(freed);

    cache.remove("a");
    EXPECT_FALSE(freed);

    cache.remove("b");
    EXPECT_FALSE(freed);
  }
  EXPECT_TRUE(freed);
}

TEST(Cache, sameKeyValueIntrusiveRefCountPter) {
  bool freed = false;
  swift::sys::Cache<const char *, llvm::IntrusiveRefCntPtr<RefCntToken>> cache(__func__);
  {
    llvm::IntrusiveRefCntPtr<RefCntToken> c(new RefCntToken(freed));
    cache.set("a", c);
    EXPECT_FALSE(freed);
    cache.set("a", c);
    EXPECT_FALSE(freed);
  }
  EXPECT_FALSE(freed);
  cache.remove("a");
  EXPECT_TRUE(freed);
}

TEST(Cache, copyValue) {
  struct S {
    int ident, copy;
    S(int ident) : ident(ident), copy(0) {}
    S(const S &other) : ident(other.ident), copy(other.copy+1) {}
    S(S &&other) : ident(other.ident), copy(other.copy) {}
  };
  swift::sys::Cache<const char *, struct S> cache(__func__);
  S s{0};
  EXPECT_EQ(0, s.ident);
  EXPECT_EQ(0, s.copy);
  cache.set("a", s);
  EXPECT_EQ(0, cache.get("a")->ident);
  EXPECT_EQ(2, cache.get("a")->copy); // return by value causes 2nd copy
  cache.set("b", *cache.get("a"));
  EXPECT_EQ(0, cache.get("b")->ident);
  EXPECT_EQ(4, cache.get("b")->copy); // return by value causes 2nd copy
}
<|endoftext|>


<commit_before>//===-- SocketTest.cpp ------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <cstdio>
#include <functional>
#include <thread>

#include "gtest/gtest.h"

#include "lldb/Host/Config.h"
#include "lldb/Host/Socket.h"
#include "lldb/Host/common/TCPSocket.h"
#include "lldb/Host/common/UDPSocket.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"

#ifndef LLDB_DISABLE_POSIX
#include "lldb/Host/posix/DomainSocket.h"
#endif

using namespace lldb_private;

class SocketTest : public testing::Test {
public:
  void SetUp() override {
#if defined(_MSC_VER)
    WSADATA data;
    ::WSAStartup(MAKEWORD(2, 2), &data);
#endif
  }

  void TearDown() override {
#if defined(_MSC_VER)
    ::WSACleanup();
#endif
  }

protected:
  static void AcceptThread(Socket *listen_socket,
                           bool child_processes_inherit, Socket **accept_socket,
                           Status *error) {
    *error = listen_socket->Accept(*accept_socket);
  }

  template <typename SocketType>
  void CreateConnectedSockets(
      llvm::StringRef listen_remote_address,
      const std::function<std::string(const SocketType &)> &get_connect_addr,
      std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up) {
    bool child_processes_inherit = false;
    Status error;
    std::unique_ptr<SocketType> listen_socket_up(
        new SocketType(true, child_processes_inherit));
    EXPECT_FALSE(error.Fail());
    error = listen_socket_up->Listen(listen_remote_address, 5);
    EXPECT_FALSE(error.Fail());
    EXPECT_TRUE(listen_socket_up->IsValid());

    Status accept_error;
    Socket *accept_socket;
    std::thread accept_thread(AcceptThread, listen_socket_up.get(),
                              child_processes_inherit, &accept_socket,
                              &accept_error);

    std::string connect_remote_address = get_connect_addr(*listen_socket_up);
    std::unique_ptr<SocketType> connect_socket_up(
        new SocketType(true, child_processes_inherit));
    EXPECT_FALSE(error.Fail());
    error = connect_socket_up->Connect(connect_remote_address);
    EXPECT_FALSE(error.Fail());
    EXPECT_TRUE(connect_socket_up->IsValid());

    a_up->swap(connect_socket_up);
    EXPECT_TRUE(error.Success());
    EXPECT_NE(nullptr, a_up->get());
    EXPECT_TRUE((*a_up)->IsValid());

    accept_thread.join();
    b_up->reset(static_cast<SocketType *>(accept_socket));
    EXPECT_TRUE(accept_error.Success());
    EXPECT_NE(nullptr, b_up->get());
    EXPECT_TRUE((*b_up)->IsValid());

    listen_socket_up.reset();
  }
};

TEST_F(SocketTest, DecodeHostAndPort) {
  std::string host_str;
  std::string port_str;
  int32_t port;
  Status error;
  EXPECT_TRUE(Socket::DecodeHostAndPort("localhost:1138", host_str, port_str,
                                        port, &error));
  EXPECT_STREQ("localhost", host_str.c_str());
  EXPECT_STREQ("1138", port_str.c_str());
  EXPECT_EQ(1138, port);
  EXPECT_TRUE(error.Success());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:65536'",
               error.AsCString());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:-1138", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:-1138'",
               error.AsCString());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:65536'",
               error.AsCString());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("12345", host_str, port_str, port, &error));
  EXPECT_STREQ("", host_str.c_str());
  EXPECT_STREQ("12345", port_str.c_str());
  EXPECT_EQ(12345, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("*:0", host_str, port_str, port, &error));
  EXPECT_STREQ("*", host_str.c_str());
  EXPECT_STREQ("0", port_str.c_str());
  EXPECT_EQ(0, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("*:65535", host_str, port_str, port, &error));
  EXPECT_STREQ("*", host_str.c_str());
  EXPECT_STREQ("65535", port_str.c_str());
  EXPECT_EQ(65535, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("[::1]:12345", host_str, port_str, port, &error));
  EXPECT_STREQ("::1", host_str.c_str());
  EXPECT_STREQ("12345", port_str.c_str());
  EXPECT_EQ(12345, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("[abcd:12fg:AF58::1]:12345", host_str, port_str, port, &error));
  EXPECT_STREQ("abcd:12fg:AF58::1", host_str.c_str());
  EXPECT_STREQ("12345", port_str.c_str());
  EXPECT_EQ(12345, port);
  EXPECT_TRUE(error.Success());
}

#ifndef LLDB_DISABLE_POSIX
TEST_F(SocketTest, DomainListenConnectAccept) {
  llvm::SmallString<64> Path;
  std::error_code EC = llvm::sys::fs::createUniqueDirectory("DomainListenConnectAccept", Path);
  ASSERT_FALSE(EC);
  llvm::sys::path::append(Path, "test");

  std::unique_ptr<DomainSocket> socket_a_up;
  std::unique_ptr<DomainSocket> socket_b_up;
  CreateConnectedSockets<DomainSocket>(
      Path, [=](const DomainSocket &) { return Path.str().str(); },
      &socket_a_up, &socket_b_up);
}
#endif

TEST_F(SocketTest, TCPListen0ConnectAccept) {
  std::unique_ptr<TCPSocket> socket_a_up;
  std::unique_ptr<TCPSocket> socket_b_up;
  CreateConnectedSockets<TCPSocket>(
      "127.0.0.1:0",
      [=](const TCPSocket &s) {
        char connect_remote_address[64];
        snprintf(connect_remote_address, sizeof(connect_remote_address),
                 "localhost:%u", s.GetLocalPortNumber());
        return std::string(connect_remote_address);
      },
      &socket_a_up, &socket_b_up);
}

TEST_F(SocketTest, TCPGetAddress) {
  std::unique_ptr<TCPSocket> socket_a_up;
  std::unique_ptr<TCPSocket> socket_b_up;
  CreateConnectedSockets<TCPSocket>(
      "127.0.0.1:0",
      [=](const TCPSocket &s) {
        char connect_remote_address[64];
        snprintf(connect_remote_address, sizeof(connect_remote_address),
                 "localhost:%u", s.GetLocalPortNumber());
        return std::string(connect_remote_address);
      },
      &socket_a_up, &socket_b_up);

  EXPECT_EQ(socket_a_up->GetLocalPortNumber(),
            socket_b_up->GetRemotePortNumber());
  EXPECT_EQ(socket_b_up->GetLocalPortNumber(),
            socket_a_up->GetRemotePortNumber());
  EXPECT_NE(socket_a_up->GetLocalPortNumber(),
            socket_b_up->GetLocalPortNumber());
  EXPECT_STREQ("127.0.0.1", socket_a_up->GetRemoteIPAddress().c_str());
  EXPECT_STREQ("127.0.0.1", socket_b_up->GetRemoteIPAddress().c_str());
}

TEST_F(SocketTest, UDPConnect) {
  Socket *socket;

  bool child_processes_inherit = false;
  auto error = UDPSocket::Connect("127.0.0.1:0", child_processes_inherit,
                                  socket);

  std::unique_ptr<Socket> socket_up(socket);

  EXPECT_TRUE(error.Success());
  EXPECT_TRUE(socket_up->IsValid());
}

TEST_F(SocketTest, TCPListen0GetPort) {
  Socket *server_socket;
  Predicate<uint16_t> port_predicate;
  port_predicate.SetValue(0, eBroadcastNever);
  Status err =
      Socket::TcpListen("10.10.12.3:0", false, server_socket, &port_predicate);
  std::unique_ptr<TCPSocket> socket_up((TCPSocket*)server_socket);
  EXPECT_TRUE(socket_up->IsValid());
  EXPECT_NE(socket_up->GetLocalPortNumber(), 0);
}
<commit_msg>Temporarily add redundant checks to better debug bot failures<commit_after>//===-- SocketTest.cpp ------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <cstdio>
#include <functional>
#include <thread>

#include "gtest/gtest.h"

#include "lldb/Host/Config.h"
#include "lldb/Host/Socket.h"
#include "lldb/Host/common/TCPSocket.h"
#include "lldb/Host/common/UDPSocket.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"

#ifndef LLDB_DISABLE_POSIX
#include "lldb/Host/posix/DomainSocket.h"
#endif

using namespace lldb_private;

class SocketTest : public testing::Test {
public:
  void SetUp() override {
#if defined(_MSC_VER)
    WSADATA data;
    ::WSAStartup(MAKEWORD(2, 2), &data);
#endif
  }

  void TearDown() override {
#if defined(_MSC_VER)
    ::WSACleanup();
#endif
  }

protected:
  static void AcceptThread(Socket *listen_socket,
                           bool child_processes_inherit, Socket **accept_socket,
                           Status *error) {
    *error = listen_socket->Accept(*accept_socket);
  }

  template <typename SocketType>
  void CreateConnectedSockets(
      llvm::StringRef listen_remote_address,
      const std::function<std::string(const SocketType &)> &get_connect_addr,
      std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up) {
    bool child_processes_inherit = false;
    Status error;
    std::unique_ptr<SocketType> listen_socket_up(
        new SocketType(true, child_processes_inherit));
    EXPECT_EQ(nullptr, error.AsCString(nullptr));
    EXPECT_FALSE(error.Fail());
    error = listen_socket_up->Listen(listen_remote_address, 5);
    EXPECT_EQ(nullptr, error.AsCString(nullptr));
    EXPECT_FALSE(error.Fail());
    EXPECT_TRUE(listen_socket_up->IsValid());

    Status accept_error;
    Socket *accept_socket;
    std::thread accept_thread(AcceptThread, listen_socket_up.get(),
                              child_processes_inherit, &accept_socket,
                              &accept_error);

    std::string connect_remote_address = get_connect_addr(*listen_socket_up);
    std::unique_ptr<SocketType> connect_socket_up(
        new SocketType(true, child_processes_inherit));
    EXPECT_EQ(nullptr, error.AsCString(nullptr));
    EXPECT_FALSE(error.Fail());
    error = connect_socket_up->Connect(connect_remote_address);
    EXPECT_EQ(nullptr, error.AsCString(nullptr));
    EXPECT_FALSE(error.Fail());
    EXPECT_TRUE(connect_socket_up->IsValid());

    a_up->swap(connect_socket_up);
    EXPECT_EQ(nullptr, error.AsCString(nullptr));
    EXPECT_TRUE(error.Success());
    EXPECT_NE(nullptr, a_up->get());
    EXPECT_TRUE((*a_up)->IsValid());

    accept_thread.join();
    b_up->reset(static_cast<SocketType *>(accept_socket));
    EXPECT_EQ(nullptr, error.AsCString(nullptr));
    EXPECT_TRUE(accept_error.Success());
    EXPECT_NE(nullptr, b_up->get());
    EXPECT_TRUE((*b_up)->IsValid());

    listen_socket_up.reset();
  }
};

TEST_F(SocketTest, DecodeHostAndPort) {
  std::string host_str;
  std::string port_str;
  int32_t port;
  Status error;
  EXPECT_TRUE(Socket::DecodeHostAndPort("localhost:1138", host_str, port_str,
                                        port, &error));
  EXPECT_STREQ("localhost", host_str.c_str());
  EXPECT_STREQ("1138", port_str.c_str());
  EXPECT_EQ(1138, port);
  EXPECT_TRUE(error.Success());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:65536'",
               error.AsCString());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:-1138", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:-1138'",
               error.AsCString());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:65536'",
               error.AsCString());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("12345", host_str, port_str, port, &error));
  EXPECT_STREQ("", host_str.c_str());
  EXPECT_STREQ("12345", port_str.c_str());
  EXPECT_EQ(12345, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("*:0", host_str, port_str, port, &error));
  EXPECT_STREQ("*", host_str.c_str());
  EXPECT_STREQ("0", port_str.c_str());
  EXPECT_EQ(0, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("*:65535", host_str, port_str, port, &error));
  EXPECT_STREQ("*", host_str.c_str());
  EXPECT_STREQ("65535", port_str.c_str());
  EXPECT_EQ(65535, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("[::1]:12345", host_str, port_str, port, &error));
  EXPECT_STREQ("::1", host_str.c_str());
  EXPECT_STREQ("12345", port_str.c_str());
  EXPECT_EQ(12345, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("[abcd:12fg:AF58::1]:12345", host_str, port_str, port, &error));
  EXPECT_STREQ("abcd:12fg:AF58::1", host_str.c_str());
  EXPECT_STREQ("12345", port_str.c_str());
  EXPECT_EQ(12345, port);
  EXPECT_TRUE(error.Success());
}

#ifndef LLDB_DISABLE_POSIX
TEST_F(SocketTest, DomainListenConnectAccept) {
  llvm::SmallString<64> Path;
  std::error_code EC = llvm::sys::fs::createUniqueDirectory("DomainListenConnectAccept", Path);
  ASSERT_FALSE(EC);
  llvm::sys::path::append(Path, "test");

  std::unique_ptr<DomainSocket> socket_a_up;
  std::unique_ptr<DomainSocket> socket_b_up;
  CreateConnectedSockets<DomainSocket>(
      Path, [=](const DomainSocket &) { return Path.str().str(); },
      &socket_a_up, &socket_b_up);
}
#endif

TEST_F(SocketTest, TCPListen0ConnectAccept) {
  std::unique_ptr<TCPSocket> socket_a_up;
  std::unique_ptr<TCPSocket> socket_b_up;
  CreateConnectedSockets<TCPSocket>(
      "127.0.0.1:0",
      [=](const TCPSocket &s) {
        char connect_remote_address[64];
        snprintf(connect_remote_address, sizeof(connect_remote_address),
                 "localhost:%u", s.GetLocalPortNumber());
        return std::string(connect_remote_address);
      },
      &socket_a_up, &socket_b_up);
}

TEST_F(SocketTest, TCPGetAddress) {
  std::unique_ptr<TCPSocket> socket_a_up;
  std::unique_ptr<TCPSocket> socket_b_up;
  CreateConnectedSockets<TCPSocket>(
      "127.0.0.1:0",
      [=](const TCPSocket &s) {
        char connect_remote_address[64];
        snprintf(connect_remote_address, sizeof(connect_remote_address),
                 "localhost:%u", s.GetLocalPortNumber());
        return std::string(connect_remote_address);
      },
      &socket_a_up, &socket_b_up);

  EXPECT_EQ(socket_a_up->GetLocalPortNumber(),
            socket_b_up->GetRemotePortNumber());
  EXPECT_EQ(socket_b_up->GetLocalPortNumber(),
            socket_a_up->GetRemotePortNumber());
  EXPECT_NE(socket_a_up->GetLocalPortNumber(),
            socket_b_up->GetLocalPortNumber());
  EXPECT_STREQ("127.0.0.1", socket_a_up->GetRemoteIPAddress().c_str());
  EXPECT_STREQ("127.0.0.1", socket_b_up->GetRemoteIPAddress().c_str());
}

TEST_F(SocketTest, UDPConnect) {
  Socket *socket;

  bool child_processes_inherit = false;
  auto error = UDPSocket::Connect("127.0.0.1:0", child_processes_inherit,
                                  socket);

  std::unique_ptr<Socket> socket_up(socket);

  EXPECT_TRUE(error.Success());
  EXPECT_TRUE(socket_up->IsValid());
}

TEST_F(SocketTest, TCPListen0GetPort) {
  Socket *server_socket;
  Predicate<uint16_t> port_predicate;
  port_predicate.SetValue(0, eBroadcastNever);
  Status err =
      Socket::TcpListen("10.10.12.3:0", false, server_socket, &port_predicate);
  std::unique_ptr<TCPSocket> socket_up((TCPSocket*)server_socket);
  EXPECT_TRUE(socket_up->IsValid());
  EXPECT_NE(socket_up->GetLocalPortNumber(), 0);
}
<|endoftext|>


<commit_before>//===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests --*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/Constants.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "gtest/gtest.h"

namespace llvm {
namespace {

TEST(VerifierTest, Branch_i1) {
  LLVMContext C;
  Module M("M", C);
  FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
  Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy));
  BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
  BasicBlock *Exit = BasicBlock::Create(C, "exit", F);
  ReturnInst::Create(C, Exit);

  // To avoid triggering an assertion in BranchInst::Create, we first create
  // a branch with an 'i1' condition ...

  Constant *False = ConstantInt::getFalse(C);
  BranchInst *BI = BranchInst::Create(Exit, Exit, False, Entry);

  // ... then use setOperand to redirect it to a value of different type.

  Constant *Zero32 = ConstantInt::get(IntegerType::get(C, 32), 0);
  BI->setOperand(0, Zero32);

  EXPECT_TRUE(verifyFunction(*F));
}

TEST(VerifierTest, InvalidRetAttribute) {
  LLVMContext C;
  Module M("M", C);
  FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);
  Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy));
  AttributeSet AS = F->getAttributes();
  F->setAttributes(AS.addAttribute(C, AttributeSet::ReturnIndex,
                                   Attribute::UWTable));

  std::string Error;
  raw_string_ostream ErrorOS(Error);
  EXPECT_TRUE(verifyModule(M, &ErrorOS));
  EXPECT_TRUE(StringRef(ErrorOS.str()).startswith(
      "Attribute 'uwtable' only applies to functions!"));
}

TEST(VerifierTest, CrossModuleRef) {
  LLVMContext C;
  Module M1("M1", C);
  Module M2("M2", C);
  Module M3("M3", C);
  FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);
  Function *F1 = cast<Function>(M1.getOrInsertFunction("foo1", FTy));
  Function *F2 = cast<Function>(M2.getOrInsertFunction("foo2", FTy));
  Function *F3 = cast<Function>(M3.getOrInsertFunction("foo3", FTy));

  BasicBlock *Entry1 = BasicBlock::Create(C, "entry", F1);
  BasicBlock *Entry3 = BasicBlock::Create(C, "entry", F3);

  // BAD: Referencing function in another module
  CallInst::Create(F2,"call",Entry1);

  // BAD: Referencing personality routine in another module
  F3->setPersonalityFn(F2);

  // Fill in the body
  Constant *ConstZero = ConstantInt::get(Type::getInt32Ty(C), 0);
  ReturnInst::Create(C, ConstZero, Entry1);
  ReturnInst::Create(C, ConstZero, Entry3);

  std::string Error;
  raw_string_ostream ErrorOS(Error);
  EXPECT_TRUE(verifyModule(M2, &ErrorOS));
  EXPECT_TRUE(StringRef(ErrorOS.str())
                  .equals("Global is used by function in a different module\n"
                          "i32 ()* @foo2\n"
                          "; ModuleID = 'M2'\n"
                          "i32 ()* @foo3\n"
                          "; ModuleID = 'M3'\n"
                          "Global is referenced in a different module!\n"
                          "i32 ()* @foo2\n"
                          "; ModuleID = 'M2'\n"
                          "  %call = call i32 @foo2()\n"
                          "i32 ()* @foo1\n"
                          "; ModuleID = 'M1'\n"));

  Error.clear();
  EXPECT_TRUE(verifyModule(M1, &ErrorOS));
  EXPECT_TRUE(StringRef(ErrorOS.str()).equals(
      "Referencing function in another module!\n"
      "  %call = call i32 @foo2()\n"
      "; ModuleID = 'M1'\n"
      "i32 ()* @foo2\n"
      "; ModuleID = 'M2'\n"));

  Error.clear();
  EXPECT_TRUE(verifyModule(M3, &ErrorOS));
  EXPECT_TRUE(StringRef(ErrorOS.str()).startswith(
      "Referencing personality function in another module!"));

  // Erase bad methods to avoid triggering an assertion failure on destruction
  F1->eraseFromParent();
  F3->eraseFromParent();
}

TEST(VerifierTest, InvalidVariableLinkage) {
  LLVMContext C;
  Module M("M", C);
  new GlobalVariable(M, Type::getInt8Ty(C), false,
                     GlobalValue::LinkOnceODRLinkage, nullptr, "Some Global");
  std::string Error;
  raw_string_ostream ErrorOS(Error);
  EXPECT_TRUE(verifyModule(M, &ErrorOS));
  EXPECT_TRUE(
      StringRef(ErrorOS.str()).startswith("Global is external, but doesn't "
                                          "have external or weak linkage!"));
}

TEST(VerifierTest, InvalidFunctionLinkage) {
  LLVMContext C;
  Module M("M", C);

  FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
  Function::Create(FTy, GlobalValue::LinkOnceODRLinkage, "foo", &M);
  std::string Error;
  raw_string_ostream ErrorOS(Error);
  EXPECT_TRUE(verifyModule(M, &ErrorOS));
  EXPECT_TRUE(
      StringRef(ErrorOS.str()).startswith("Global is external, but doesn't "
                                          "have external or weak linkage!"));
}

#ifndef _MSC_VER
// FIXME: This test causes an ICE in MSVC 2013.
TEST(VerifierTest, StripInvalidDebugInfo) {
  {
    LLVMContext C;
    Module M("M", C);
    DIBuilder DIB(M);
    DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/", "unittest",
                          false, "", 0);
    DIB.finalize();
    EXPECT_FALSE(verifyModule(M));

    // Now break it by inserting non-CU node to the list of CUs.
    auto *File = DIB.createFile("not-a-CU.f", ".");
    NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");
    NMD->addOperand(File);
    EXPECT_TRUE(verifyModule(M));

    ModulePassManager MPM(true);
    MPM.addPass(VerifierPass(false));
    ModuleAnalysisManager MAM(true);
    MAM.registerPass([&] { return VerifierAnalysis(); });
    MPM.run(M, MAM);
    EXPECT_FALSE(verifyModule(M));
  }
  {
    LLVMContext C;
    Module M("M", C);
    DIBuilder DIB(M);
    auto *CU = DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/",
                                     "unittest", false, "", 0);
    new GlobalVariable(M, Type::getInt8Ty(C), false,
                       GlobalValue::ExternalLinkage, nullptr, "g");

    auto *F = cast<Function>(M.getOrInsertFunction(
        "f", FunctionType::get(Type::getVoidTy(C), false)));
    IRBuilder<> Builder(BasicBlock::Create(C, "", F));
    Builder.CreateUnreachable();
    F->setSubprogram(DIB.createFunction(CU, "f", "f",
                                        DIB.createFile("broken.c", "/"), 1,
                                        nullptr, true, true, 1));
    DIB.finalize();
    EXPECT_FALSE(verifyModule(M));

    // Now break it by not listing the CU at all.
    M.eraseNamedMetadata(M.getOrInsertNamedMetadata("llvm.dbg.cu"));
    EXPECT_TRUE(verifyModule(M));

    ModulePassManager MPM(true);
    MPM.addPass(VerifierPass(false));
    ModuleAnalysisManager MAM(true);
    MAM.registerPass([&] { return VerifierAnalysis(); });
    MPM.run(M, MAM);
    EXPECT_FALSE(verifyModule(M));
  }
}
#endif

TEST(VerifierTest, StripInvalidDebugInfoLegacy) {
  LLVMContext C;
  Module M("M", C);
  DIBuilder DIB(M);
  DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/",
                        "unittest", false, "", 0);
  DIB.finalize();
  EXPECT_FALSE(verifyModule(M));

  // Now break it.
  auto *File = DIB.createFile("not-a-CU.f", ".");
  NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");
  NMD->addOperand(File);
  EXPECT_TRUE(verifyModule(M));

  legacy::PassManager Passes;
  Passes.add(createVerifierPass(false));
  Passes.run(M);
  EXPECT_FALSE(verifyModule(M));
}

} // end anonymous namespace
} // end namespace llvm
<commit_msg>[unittests] Remove an MSVC 2013 workaround, NFCI.<commit_after>//===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests --*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/Constants.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "gtest/gtest.h"

namespace llvm {
namespace {

TEST(VerifierTest, Branch_i1) {
  LLVMContext C;
  Module M("M", C);
  FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
  Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy));
  BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
  BasicBlock *Exit = BasicBlock::Create(C, "exit", F);
  ReturnInst::Create(C, Exit);

  // To avoid triggering an assertion in BranchInst::Create, we first create
  // a branch with an 'i1' condition ...

  Constant *False = ConstantInt::getFalse(C);
  BranchInst *BI = BranchInst::Create(Exit, Exit, False, Entry);

  // ... then use setOperand to redirect it to a value of different type.

  Constant *Zero32 = ConstantInt::get(IntegerType::get(C, 32), 0);
  BI->setOperand(0, Zero32);

  EXPECT_TRUE(verifyFunction(*F));
}

TEST(VerifierTest, InvalidRetAttribute) {
  LLVMContext C;
  Module M("M", C);
  FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);
  Function *F = cast<Function>(M.getOrInsertFunction("foo", FTy));
  AttributeSet AS = F->getAttributes();
  F->setAttributes(AS.addAttribute(C, AttributeSet::ReturnIndex,
                                   Attribute::UWTable));

  std::string Error;
  raw_string_ostream ErrorOS(Error);
  EXPECT_TRUE(verifyModule(M, &ErrorOS));
  EXPECT_TRUE(StringRef(ErrorOS.str()).startswith(
      "Attribute 'uwtable' only applies to functions!"));
}

TEST(VerifierTest, CrossModuleRef) {
  LLVMContext C;
  Module M1("M1", C);
  Module M2("M2", C);
  Module M3("M3", C);
  FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);
  Function *F1 = cast<Function>(M1.getOrInsertFunction("foo1", FTy));
  Function *F2 = cast<Function>(M2.getOrInsertFunction("foo2", FTy));
  Function *F3 = cast<Function>(M3.getOrInsertFunction("foo3", FTy));

  BasicBlock *Entry1 = BasicBlock::Create(C, "entry", F1);
  BasicBlock *Entry3 = BasicBlock::Create(C, "entry", F3);

  // BAD: Referencing function in another module
  CallInst::Create(F2,"call",Entry1);

  // BAD: Referencing personality routine in another module
  F3->setPersonalityFn(F2);

  // Fill in the body
  Constant *ConstZero = ConstantInt::get(Type::getInt32Ty(C), 0);
  ReturnInst::Create(C, ConstZero, Entry1);
  ReturnInst::Create(C, ConstZero, Entry3);

  std::string Error;
  raw_string_ostream ErrorOS(Error);
  EXPECT_TRUE(verifyModule(M2, &ErrorOS));
  EXPECT_TRUE(StringRef(ErrorOS.str())
                  .equals("Global is used by function in a different module\n"
                          "i32 ()* @foo2\n"
                          "; ModuleID = 'M2'\n"
                          "i32 ()* @foo3\n"
                          "; ModuleID = 'M3'\n"
                          "Global is referenced in a different module!\n"
                          "i32 ()* @foo2\n"
                          "; ModuleID = 'M2'\n"
                          "  %call = call i32 @foo2()\n"
                          "i32 ()* @foo1\n"
                          "; ModuleID = 'M1'\n"));

  Error.clear();
  EXPECT_TRUE(verifyModule(M1, &ErrorOS));
  EXPECT_TRUE(StringRef(ErrorOS.str()).equals(
      "Referencing function in another module!\n"
      "  %call = call i32 @foo2()\n"
      "; ModuleID = 'M1'\n"
      "i32 ()* @foo2\n"
      "; ModuleID = 'M2'\n"));

  Error.clear();
  EXPECT_TRUE(verifyModule(M3, &ErrorOS));
  EXPECT_TRUE(StringRef(ErrorOS.str()).startswith(
      "Referencing personality function in another module!"));

  // Erase bad methods to avoid triggering an assertion failure on destruction
  F1->eraseFromParent();
  F3->eraseFromParent();
}

TEST(VerifierTest, InvalidVariableLinkage) {
  LLVMContext C;
  Module M("M", C);
  new GlobalVariable(M, Type::getInt8Ty(C), false,
                     GlobalValue::LinkOnceODRLinkage, nullptr, "Some Global");
  std::string Error;
  raw_string_ostream ErrorOS(Error);
  EXPECT_TRUE(verifyModule(M, &ErrorOS));
  EXPECT_TRUE(
      StringRef(ErrorOS.str()).startswith("Global is external, but doesn't "
                                          "have external or weak linkage!"));
}

TEST(VerifierTest, InvalidFunctionLinkage) {
  LLVMContext C;
  Module M("M", C);

  FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
  Function::Create(FTy, GlobalValue::LinkOnceODRLinkage, "foo", &M);
  std::string Error;
  raw_string_ostream ErrorOS(Error);
  EXPECT_TRUE(verifyModule(M, &ErrorOS));
  EXPECT_TRUE(
      StringRef(ErrorOS.str()).startswith("Global is external, but doesn't "
                                          "have external or weak linkage!"));
}

TEST(VerifierTest, StripInvalidDebugInfo) {
  {
    LLVMContext C;
    Module M("M", C);
    DIBuilder DIB(M);
    DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/", "unittest",
                          false, "", 0);
    DIB.finalize();
    EXPECT_FALSE(verifyModule(M));

    // Now break it by inserting non-CU node to the list of CUs.
    auto *File = DIB.createFile("not-a-CU.f", ".");
    NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");
    NMD->addOperand(File);
    EXPECT_TRUE(verifyModule(M));

    ModulePassManager MPM(true);
    MPM.addPass(VerifierPass(false));
    ModuleAnalysisManager MAM(true);
    MAM.registerPass([&] { return VerifierAnalysis(); });
    MPM.run(M, MAM);
    EXPECT_FALSE(verifyModule(M));
  }
  {
    LLVMContext C;
    Module M("M", C);
    DIBuilder DIB(M);
    auto *CU = DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/",
                                     "unittest", false, "", 0);
    new GlobalVariable(M, Type::getInt8Ty(C), false,
                       GlobalValue::ExternalLinkage, nullptr, "g");

    auto *F = cast<Function>(M.getOrInsertFunction(
        "f", FunctionType::get(Type::getVoidTy(C), false)));
    IRBuilder<> Builder(BasicBlock::Create(C, "", F));
    Builder.CreateUnreachable();
    F->setSubprogram(DIB.createFunction(CU, "f", "f",
                                        DIB.createFile("broken.c", "/"), 1,
                                        nullptr, true, true, 1));
    DIB.finalize();
    EXPECT_FALSE(verifyModule(M));

    // Now break it by not listing the CU at all.
    M.eraseNamedMetadata(M.getOrInsertNamedMetadata("llvm.dbg.cu"));
    EXPECT_TRUE(verifyModule(M));

    ModulePassManager MPM(true);
    MPM.addPass(VerifierPass(false));
    ModuleAnalysisManager MAM(true);
    MAM.registerPass([&] { return VerifierAnalysis(); });
    MPM.run(M, MAM);
    EXPECT_FALSE(verifyModule(M));
  }
}

TEST(VerifierTest, StripInvalidDebugInfoLegacy) {
  LLVMContext C;
  Module M("M", C);
  DIBuilder DIB(M);
  DIB.createCompileUnit(dwarf::DW_LANG_C89, "broken.c", "/",
                        "unittest", false, "", 0);
  DIB.finalize();
  EXPECT_FALSE(verifyModule(M));

  // Now break it.
  auto *File = DIB.createFile("not-a-CU.f", ".");
  NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");
  NMD->addOperand(File);
  EXPECT_TRUE(verifyModule(M));

  legacy::PassManager Passes;
  Passes.add(createVerifierPass(false));
  Passes.run(M);
  EXPECT_FALSE(verifyModule(M));
}

} // end anonymous namespace
} // end namespace llvm
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////
// Licensed to Qualys, Inc. (QUALYS) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// QUALYS licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/// @file
/// @brief IronBee --- Lock Test Functions
///
/// @author Nick LeRoy <nleroy@qualys.com>
//////////////////////////////////////////////////////////////////////////////

#include "ironbee_config_auto.h"

#include <ironbee/types.h>
#include <ironbee/mm.h>
#include <ironbee/util.h>
#include <ironbee/lock.h>

#include "gtest/gtest.h"
#include "simple_fixture.hpp"

#include <stdexcept>
#include <math.h>
#include <pthread.h>

using namespace std;

/* -- Tests -- */
typedef void *(* thread_start_fn)(void *thread_data);

class Thread
{
public:
    explicit
    Thread(int num) :
        m_handle(0),
        m_num(num),
        m_started(false),
        m_running(false),
        m_errors(0)
    {
    }
    Thread() :
        m_handle(0),
        m_num(-1),
        m_started(false),
        m_running(false),
        m_errors(0)
    {
    }

    void ThreadNum(int num) { m_num = num; };
    bool IsRunning() const { return m_running; };
    int ThreadNum() const { return m_num; };
    uint64_t ThreadId() const { return (uint64_t)m_handle; };
    uint64_t Errors() const { return m_errors; };
    void Error() { ++m_errors; };

    ib_status_t Create(thread_start_fn fn)
    {
        pthread_t      handle;
        pthread_attr_t attr;
        int            status;

        if (m_num < 0) {
            return IB_EINVAL;
        }
        if (m_started || m_running) {
            return IB_EINVAL;
        }

        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

        m_started = true;
        status = pthread_create(&handle, &attr, fn, this);
        if (status) {
            m_started = false;
            return IB_EUNKNOWN;
        }

        m_handle = handle;
        return IB_OK;
    }

    ib_status_t Join()
    {
        int         status;

        if (! m_started) {
            return IB_OK;
        }
        status = pthread_join(m_handle, NULL);
        if (status != 0) {
            return IB_EUNKNOWN;
        }
        m_running = false;
        m_started = false;

        return IB_OK;
    }

    ib_status_t Running(bool run)
    {
        if (!m_started) {
            return IB_EINVAL;
        }
        if (run == m_running) {
            return IB_EINVAL;
        }
        m_running = run;
        return IB_OK;
    }

private:
    pthread_t        m_handle;
    int              m_num;
    bool             m_started;
    bool             m_running;
    uint64_t         m_errors;
};

class TestIBUtilLock : public SimpleFixture
{
public:
    TestIBUtilLock() :
        m_max_threads(0),
        m_threads(NULL),
        m_lock_enabled(true),
        m_shared(0)
    {
        TestIBUtilLock::m_self = this;
        TestParams(100, 0.0005, true);
    }
    ~TestIBUtilLock()
    {
        if (m_threads != NULL) {
            delete []m_threads;
        }
    }

    virtual void SetUp()
    {
        SimpleFixture::SetUp();
    }
    virtual void TearDown()
    {
        SimpleFixture::TearDown();
        DestroyLock( );
    }

    ib_status_t CreateLock()
    {
        return ib_lock_create_malloc(&m_lock);
    }
    ib_status_t DestroyLock()
    {
        ib_lock_destroy_malloc(m_lock);
        return IB_OK;
    }
    ib_status_t LockLock()
    {
        return ib_lock_lock(m_lock);
    }
    ib_status_t UnlockLock()
    {
        return ib_lock_unlock(m_lock);
    }

    void InitThreads(size_t max_threads)
    {
        m_max_threads = max_threads;
        m_threads = new Thread[max_threads];

        for (int num = 0;  num < (int)max_threads;  ++num) {
            m_threads[num].ThreadNum(num);
        }

    }
    void TestParams(size_t loops, double seconds, bool lock)
    {
        Loops(loops);
        SleepTime(seconds);
        m_lock_enabled = lock;
    }

    void Loops(size_t loops) { m_loops = loops; };
    void SleepTime(double sec) { m_sleeptime = sec; };

    ib_status_t CreateThread(size_t num)
    {
        if(m_threads == NULL) {
            throw std::runtime_error("Thread handles not initialized.");
        }
        if (num >= m_max_threads) {
            throw std::runtime_error("Thread number greater than max.");
        }
        Thread *thread = &m_threads[num];
        if (thread->IsRunning()) {
            throw std::runtime_error("Thread already running.");
        }
        return thread->Create(TestIBUtilLock::StartThread);
    }

    ib_status_t CreateAllThreads()
    {
        if(m_threads == NULL) {
            throw std::runtime_error("Thread handles not initialized.");
        }

        size_t num;
        for (num = 0;  num < m_max_threads;  ++num) {
            ib_status_t rc = CreateThread(num);
            if (rc != IB_OK) {
                return rc;
            }
        }
        return IB_OK;
    }

    ib_status_t StartTest(size_t threads,
                          size_t loops,
                          double seconds,
                          bool lock)
    {
        InitThreads(threads);
        TestParams(loops, seconds, lock);
        printf("Starting: %zd threads, %d loops, %.8fs sleep, locks %s\n",
               m_max_threads, m_loops, m_sleeptime,
               m_lock_enabled ? "enabled" : "disabled");
        return CreateAllThreads( );
    }

    static void *StartThread(void *data)
    {
        Thread *thread = (Thread *)data;
        m_self->RunThread(thread);
        return NULL;
    }

    void RunThread(Thread *thread)
    {
        ib_status_t     rc;
        int             n;
        struct timespec ts;

        ts.tv_sec = (time_t)trunc(m_sleeptime);
        ts.tv_nsec = (long)(round((m_sleeptime - ts.tv_sec) * 1e9));

        thread->Running(true);

        for (n = 0;  n < m_loops;  ++n) {
            if (m_lock_enabled) {
                rc = LockLock( );
                if (rc != IB_OK) {
                    thread->Error();
                    break;
                }
            }
            // This code is an intentional race condition if m_lock_enabled is
            // false.  It is possible for it to fail to cause errors, but, at
            // least in common environments, that is very unlikely.
            if (++m_shared != 1) {
                thread->Error();
            }
            nanosleep(&ts, NULL);
            if (--m_shared != 0) {
                thread->Error();
            }

            if (m_lock_enabled) {
                rc = UnlockLock( );
                if (rc != IB_OK) {
                    thread->Error();
                    break;
                }
            }
        }
        thread->Running(false);
    }

    ib_status_t WaitForThreads(uint64_t *errors)
    {
        ib_status_t rc = IB_OK;
        size_t num;

        *errors = 0;
        for (num = 0;  num < m_max_threads;  ++num) {
            Thread *thread = &m_threads[num];
            ib_status_t irc = thread->Join();
            if (irc != IB_OK) {
                rc = irc;
            }
            *errors += thread->Errors();
        }
        return rc;
    }

private:
    static TestIBUtilLock *m_self;
    size_t                 m_max_threads;
    Thread                *m_threads;
    ib_lock_t             *m_lock;
    bool                   m_lock_enabled;
    int                    m_loops;
    double                 m_sleeptime;
    volatile int           m_shared;
};
TestIBUtilLock *TestIBUtilLock::m_self = NULL;

TEST(test_util_lock, misc)
{
    int test = 0;

    for (int n=0; n<100; ++n) {
        int t;
        t = ++test;
        ASSERT_EQ(1, t);
        t = --test;
        ASSERT_EQ(0, t);
    }
}

TEST_F(TestIBUtilLock, test_create)
{
    ib_status_t rc;

    rc = CreateLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = LockLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = UnlockLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = DestroyLock( );
    ASSERT_EQ(IB_OK, rc);
}

// The following test is a true positive for a thread race condition.
// Disable it for thread sanitizer.
TEST_F(TestIBUtilLock, test_lock_disabled)
{
#ifdef IB_THREAD_SANITIZER_WORKAROUND
    cout << "Test skipped due to thread sanitizer." << endl;
    return;
#else
    ib_status_t rc;
    uint64_t    errors;

    rc = CreateLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = StartTest(5, 100, 0.0000005, false);
    ASSERT_EQ(IB_OK, rc);

    rc = WaitForThreads( &errors );
    ASSERT_EQ(IB_OK, rc);
    ASSERT_NE(0LU, errors);

    rc = DestroyLock( );
    ASSERT_EQ(IB_OK, rc);
#endif
}

TEST_F(TestIBUtilLock, test_short)
{
    ib_status_t rc;
    uint64_t    errors;

    rc = CreateLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = StartTest(5, 100, 0.0000005, true);
    ASSERT_EQ(IB_OK, rc);

    rc = WaitForThreads( &errors );
    ASSERT_EQ(IB_OK, rc);
    ASSERT_EQ(0LU, errors);

    rc = DestroyLock( );
    ASSERT_EQ(IB_OK, rc);
}

// This test is too intense for the thread sanitizer.
TEST_F(TestIBUtilLock, test_long)
{
#ifdef IB_THREAD_SANITIZER_WORKAROUND
    cout << "Test skipped due to thread sanitizer." << endl;
    return;
#else
    ib_status_t rc;
    uint64_t    errors;

    rc = CreateLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = StartTest(20, 1000, 0.00005, true);
    ASSERT_EQ(IB_OK, rc);

    rc = WaitForThreads( &errors );
    ASSERT_EQ(IB_OK, rc);
    ASSERT_EQ(0LU, errors);

    rc = DestroyLock( );
    ASSERT_EQ(IB_OK, rc);
#endif
}
<commit_msg>test_util_lock.cpp: Remove a double-free for a test mutex.<commit_after>//////////////////////////////////////////////////////////////////////////////
// Licensed to Qualys, Inc. (QUALYS) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// QUALYS licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/// @file
/// @brief IronBee --- Lock Test Functions
///
/// @author Nick LeRoy <nleroy@qualys.com>
//////////////////////////////////////////////////////////////////////////////

#include "ironbee_config_auto.h"

#include <ironbee/types.h>
#include <ironbee/mm.h>
#include <ironbee/util.h>
#include <ironbee/lock.h>

#include "gtest/gtest.h"
#include "simple_fixture.hpp"

#include <stdexcept>
#include <math.h>
#include <pthread.h>

using namespace std;

/* -- Tests -- */
typedef void *(* thread_start_fn)(void *thread_data);

class Thread
{
public:
    explicit
    Thread(int num) :
        m_handle(0),
        m_num(num),
        m_started(false),
        m_running(false),
        m_errors(0)
    {
    }
    Thread() :
        m_handle(0),
        m_num(-1),
        m_started(false),
        m_running(false),
        m_errors(0)
    {
    }

    void ThreadNum(int num) { m_num = num; };
    bool IsRunning() const { return m_running; };
    int ThreadNum() const { return m_num; };
    uint64_t ThreadId() const { return (uint64_t)m_handle; };
    uint64_t Errors() const { return m_errors; };
    void Error() { ++m_errors; };

    ib_status_t Create(thread_start_fn fn)
    {
        pthread_t      handle;
        pthread_attr_t attr;
        int            status;

        if (m_num < 0) {
            return IB_EINVAL;
        }
        if (m_started || m_running) {
            return IB_EINVAL;
        }

        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

        m_started = true;
        status = pthread_create(&handle, &attr, fn, this);
        if (status) {
            m_started = false;
            return IB_EUNKNOWN;
        }

        m_handle = handle;
        return IB_OK;
    }

    ib_status_t Join()
    {
        int         status;

        if (! m_started) {
            return IB_OK;
        }
        status = pthread_join(m_handle, NULL);
        if (status != 0) {
            return IB_EUNKNOWN;
        }
        m_running = false;
        m_started = false;

        return IB_OK;
    }

    ib_status_t Running(bool run)
    {
        if (!m_started) {
            return IB_EINVAL;
        }
        if (run == m_running) {
            return IB_EINVAL;
        }
        m_running = run;
        return IB_OK;
    }

private:
    pthread_t        m_handle;
    int              m_num;
    bool             m_started;
    bool             m_running;
    uint64_t         m_errors;
};

class TestIBUtilLock : public SimpleFixture
{
public:
    TestIBUtilLock() :
        m_max_threads(0),
        m_threads(NULL),
        m_lock_enabled(true),
        m_shared(0)
    {
        TestIBUtilLock::m_self = this;
        TestParams(100, 0.0005, true);
    }
    ~TestIBUtilLock()
    {
        if (m_threads != NULL) {
            delete []m_threads;
        }
    }

    virtual void SetUp()
    {
        SimpleFixture::SetUp();
    }
    virtual void TearDown()
    {
        SimpleFixture::TearDown();
        DestroyLock( );
    }

    ib_status_t CreateLock()
    {
        return ib_lock_create_malloc(&m_lock);
    }
    ib_status_t DestroyLock()
    {
        ib_lock_destroy_malloc(m_lock);
        m_lock = NULL;
        return IB_OK;
    }
    ib_status_t LockLock()
    {
        return ib_lock_lock(m_lock);
    }
    ib_status_t UnlockLock()
    {
        return ib_lock_unlock(m_lock);
    }

    void InitThreads(size_t max_threads)
    {
        m_max_threads = max_threads;
        m_threads = new Thread[max_threads];

        for (int num = 0;  num < (int)max_threads;  ++num) {
            m_threads[num].ThreadNum(num);
        }

    }
    void TestParams(size_t loops, double seconds, bool lock)
    {
        Loops(loops);
        SleepTime(seconds);
        m_lock_enabled = lock;
    }

    void Loops(size_t loops) { m_loops = loops; };
    void SleepTime(double sec) { m_sleeptime = sec; };

    ib_status_t CreateThread(size_t num)
    {
        if(m_threads == NULL) {
            throw std::runtime_error("Thread handles not initialized.");
        }
        if (num >= m_max_threads) {
            throw std::runtime_error("Thread number greater than max.");
        }
        Thread *thread = &m_threads[num];
        if (thread->IsRunning()) {
            throw std::runtime_error("Thread already running.");
        }
        return thread->Create(TestIBUtilLock::StartThread);
    }

    ib_status_t CreateAllThreads()
    {
        if(m_threads == NULL) {
            throw std::runtime_error("Thread handles not initialized.");
        }

        size_t num;
        for (num = 0;  num < m_max_threads;  ++num) {
            ib_status_t rc = CreateThread(num);
            if (rc != IB_OK) {
                return rc;
            }
        }
        return IB_OK;
    }

    ib_status_t StartTest(size_t threads,
                          size_t loops,
                          double seconds,
                          bool lock)
    {
        InitThreads(threads);
        TestParams(loops, seconds, lock);
        printf("Starting: %zd threads, %d loops, %.8fs sleep, locks %s\n",
               m_max_threads, m_loops, m_sleeptime,
               m_lock_enabled ? "enabled" : "disabled");
        return CreateAllThreads( );
    }

    static void *StartThread(void *data)
    {
        Thread *thread = (Thread *)data;
        m_self->RunThread(thread);
        return NULL;
    }

    void RunThread(Thread *thread)
    {
        ib_status_t     rc;
        int             n;
        struct timespec ts;

        ts.tv_sec = (time_t)trunc(m_sleeptime);
        ts.tv_nsec = (long)(round((m_sleeptime - ts.tv_sec) * 1e9));

        thread->Running(true);

        for (n = 0;  n < m_loops;  ++n) {
            if (m_lock_enabled) {
                rc = LockLock( );
                if (rc != IB_OK) {
                    thread->Error();
                    break;
                }
            }
            // This code is an intentional race condition if m_lock_enabled is
            // false.  It is possible for it to fail to cause errors, but, at
            // least in common environments, that is very unlikely.
            if (++m_shared != 1) {
                thread->Error();
            }
            nanosleep(&ts, NULL);
            if (--m_shared != 0) {
                thread->Error();
            }

            if (m_lock_enabled) {
                rc = UnlockLock( );
                if (rc != IB_OK) {
                    thread->Error();
                    break;
                }
            }
        }
        thread->Running(false);
    }

    ib_status_t WaitForThreads(uint64_t *errors)
    {
        ib_status_t rc = IB_OK;
        size_t num;

        *errors = 0;
        for (num = 0;  num < m_max_threads;  ++num) {
            Thread *thread = &m_threads[num];
            ib_status_t irc = thread->Join();
            if (irc != IB_OK) {
                rc = irc;
            }
            *errors += thread->Errors();
        }
        return rc;
    }

private:
    static TestIBUtilLock *m_self;
    size_t                 m_max_threads;
    Thread                *m_threads;
    ib_lock_t             *m_lock;
    bool                   m_lock_enabled;
    int                    m_loops;
    double                 m_sleeptime;
    volatile int           m_shared;
};
TestIBUtilLock *TestIBUtilLock::m_self = NULL;

TEST(test_util_lock, misc)
{
    int test = 0;

    for (int n=0; n<100; ++n) {
        int t;
        t = ++test;
        ASSERT_EQ(1, t);
        t = --test;
        ASSERT_EQ(0, t);
    }
}

TEST_F(TestIBUtilLock, test_create)
{
    ib_status_t rc;

    rc = CreateLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = LockLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = UnlockLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = DestroyLock( );
    ASSERT_EQ(IB_OK, rc);
}

// The following test is a true positive for a thread race condition.
// Disable it for thread sanitizer.
TEST_F(TestIBUtilLock, test_lock_disabled)
{
#ifdef IB_THREAD_SANITIZER_WORKAROUND
    cout << "Test skipped due to thread sanitizer." << endl;
    return;
#else
    ib_status_t rc;
    uint64_t    errors;

    rc = CreateLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = StartTest(5, 100, 0.0000005, false);
    ASSERT_EQ(IB_OK, rc);

    rc = WaitForThreads( &errors );
    ASSERT_EQ(IB_OK, rc);
    ASSERT_NE(0LU, errors);

    rc = DestroyLock( );
    ASSERT_EQ(IB_OK, rc);
#endif
}

TEST_F(TestIBUtilLock, test_short)
{
    ib_status_t rc;
    uint64_t    errors;

    rc = CreateLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = StartTest(5, 100, 0.0000005, true);
    ASSERT_EQ(IB_OK, rc);

    rc = WaitForThreads( &errors );
    ASSERT_EQ(IB_OK, rc);
    ASSERT_EQ(0LU, errors);

    rc = DestroyLock( );
    ASSERT_EQ(IB_OK, rc);
}

// This test is too intense for the thread sanitizer.
TEST_F(TestIBUtilLock, test_long)
{
#ifdef IB_THREAD_SANITIZER_WORKAROUND
    cout << "Test skipped due to thread sanitizer." << endl;
    return;
#else
    ib_status_t rc;
    uint64_t    errors;

    rc = CreateLock( );
    ASSERT_EQ(IB_OK, rc);

    rc = StartTest(20, 1000, 0.00005, true);
    ASSERT_EQ(IB_OK, rc);

    rc = WaitForThreads( &errors );
    ASSERT_EQ(IB_OK, rc);
    ASSERT_EQ(0LU, errors);

    rc = DestroyLock( );
    ASSERT_EQ(IB_OK, rc);
#endif
}
<|endoftext|>