<commit_before>#ifndef NANODBC_TEST_BASE_TEST_FIXTURE_H
#define NANODBC_TEST_BASE_TEST_FIXTURE_H

#include "nanodbc.h"
#include <tuple>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244) // conversion from 'T1' to 'T2' possible loss of data
#endif

#if defined(_MSC_VER) && _MSC_VER <= 1800
    // These versions of Visual C++ do not yet support noexcept or override.
    #define NANODBC_NOEXCEPT
    #define NANODBC_OVERRIDE
#else
    #define NANODBC_NOEXCEPT noexcept
    #define NANODBC_OVERRIDE override
#endif

#ifdef NANODBC_USE_UNICODE
    #define NANODBC_TEXT(s) L ## s
#else
    #define NANODBC_TEXT(s) s
#endif

#ifdef NANODBC_USE_BOOST_CONVERT
    #include <boost/locale/encoding_utf.hpp>
#else
    #include <codecvt>
#endif

#ifdef _WIN32
// needs to be included above sql.h for windows
#define NOMINMAX
#include <windows.h>
#endif
#include <sql.h>
#include <sqlext.h>

struct base_test_fixture
{
    // To invoke a unit test over all integral types, use:
    //
    typedef std::tuple<
        nanodbc::string_type::value_type
        , short
        , unsigned short
        , int32_t
        , uint32_t
        , int64_t
        , uint64_t
        , float
        , double
        > integral_test_types;

    base_test_fixture()
    : connection_string_(get_connection_string_from_env())
    {
    }

    base_test_fixture(const nanodbc::string_type& connection_string)
    : connection_string_(connection_string)
    {
    }

    virtual ~base_test_fixture() NANODBC_NOEXCEPT
    {
    }

    // Utilities

    nanodbc::string_type connection_string_;

    nanodbc::connection connect()
    {
        return nanodbc::connection(connection_string_);
    }

    static void check_rows_equal(nanodbc::result results, int rows)
    {
        REQUIRE(results.next());
        REQUIRE(results.get<int>(0) == rows);
    }

    nanodbc::string_type get_connection_string_from_env() const
    {
        const char* env_name = "NANODBC_TEST_CONNSTR";
        char* env_value = nullptr;
        std::string connection_string;
        #ifdef _MSC_VER
            std::size_t env_len(0);
            errno_t err = _dupenv_s(&env_value, &env_len, env_name);
            if(!err && env_value)
            {
                connection_string = env_value;
                std::free(env_value);
            }
        #else
            env_value = std::getenv(env_name);
            if(!env_value) return nanodbc::string_type();
            connection_string = env_value;
        #endif

        #ifdef NANODBC_USE_UNICODE
            #ifdef NANODBC_USE_BOOST_CONVERT
                using boost::locale::conv::utf_to_utf;
                return utf_to_utf<wchar_t>(
                    connection_string.c_str()
                    , connection_string.c_str() + connection_string.size());
            #else
                return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(connection_string);
            #endif
        #else
                return connection_string;
        #endif
    }

    virtual void drop_table(nanodbc::connection& connection, const nanodbc::string_type& name) const
    {
        execute(connection, NANODBC_TEXT("DROP TABLE IF EXISTS ") + name + NANODBC_TEXT(";"));
    }

    // Test Cases

    void catalog_columns_test()
    {
        nanodbc::connection connection = connect();
        REQUIRE(connection.connected());
        nanodbc::catalog catalog(connection);

        // TODO: Tested against SQL Server only and it is failing against PostgreSQL
        //       We are going to need postgresql_test.cpp, as we do with sqlite_test.cpp.
        //       It also seems to fail for MySQL as well. -- lexicalunit

        // Check we can iterate over any columns
        {
            nanodbc::catalog::columns columns = catalog.find_columns();<commit_msg>Propose more portable test for a table presence.<commit_after>#ifndef NANODBC_TEST_BASE_TEST_FIXTURE_H
#define NANODBC_TEST_BASE_TEST_FIXTURE_H

#include "nanodbc.h"
#include <tuple>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244) // conversion from 'T1' to 'T2' possible loss of data
#endif

#if defined(_MSC_VER) && _MSC_VER <= 1800
    // These versions of Visual C++ do not yet support noexcept or override.
    #define NANODBC_NOEXCEPT
    #define NANODBC_OVERRIDE
#else
    #define NANODBC_NOEXCEPT noexcept
    #define NANODBC_OVERRIDE override
#endif

#ifdef NANODBC_USE_UNICODE
    #define NANODBC_TEXT(s) L ## s
#else
    #define NANODBC_TEXT(s) s
#endif

#ifdef NANODBC_USE_BOOST_CONVERT
    #include <boost/locale/encoding_utf.hpp>
#else
    #include <codecvt>
#endif

#ifdef _WIN32
// needs to be included above sql.h for windows
#define NOMINMAX
#include <windows.h>
#endif
#include <sql.h>
#include <sqlext.h>

struct base_test_fixture
{
    // To invoke a unit test over all integral types, use:
    //
    typedef std::tuple<
        nanodbc::string_type::value_type
        , short
        , unsigned short
        , int32_t
        , uint32_t
        , int64_t
        , uint64_t
        , float
        , double
        > integral_test_types;

    base_test_fixture()
    : connection_string_(get_connection_string_from_env())
    {
    }

    base_test_fixture(const nanodbc::string_type& connection_string)
    : connection_string_(connection_string)
    {
    }

    virtual ~base_test_fixture() NANODBC_NOEXCEPT
    {
    }

    // Utilities

    nanodbc::string_type connection_string_;

    nanodbc::connection connect()
    {
        return nanodbc::connection(connection_string_);
    }

    static void check_rows_equal(nanodbc::result results, int rows)
    {
        REQUIRE(results.next());
        REQUIRE(results.get<int>(0) == rows);
    }

    nanodbc::string_type get_connection_string_from_env() const
    {
        const char* env_name = "NANODBC_TEST_CONNSTR";
        char* env_value = nullptr;
        std::string connection_string;
        #ifdef _MSC_VER
            std::size_t env_len(0);
            errno_t err = _dupenv_s(&env_value, &env_len, env_name);
            if(!err && env_value)
            {
                connection_string = env_value;
                std::free(env_value);
            }
        #else
            env_value = std::getenv(env_name);
            if(!env_value) return nanodbc::string_type();
            connection_string = env_value;
        #endif

        #ifdef NANODBC_USE_UNICODE
            #ifdef NANODBC_USE_BOOST_CONVERT
                using boost::locale::conv::utf_to_utf;
                return utf_to_utf<wchar_t>(
                    connection_string.c_str()
                    , connection_string.c_str() + connection_string.size());
            #else
                return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(connection_string);
            #endif
        #else
                return connection_string;
        #endif
    }

    virtual void drop_table(nanodbc::connection& connection, const nanodbc::string_type& name) const
    {
        try
        {
            // create empty result set as a poor man's portable "IF EXISTS" test
            nanodbc::result results = execute(connection,
                NANODBC_TEXT("SELECT * FROM ") + name + NANODBC_TEXT(" WHERE 0=1;"));
            execute(connection, NANODBC_TEXT("DROP TABLE ") + name + NANODBC_TEXT(";"));
        }
        catch (...)
        {
            ; // assume table does not exist
        }
    }

    // Test Cases

    void catalog_columns_test()
    {
        nanodbc::connection connection = connect();
        REQUIRE(connection.connected());
        nanodbc::catalog catalog(connection);

        // TODO: Tested against SQL Server only and it is failing against PostgreSQL
        //       We are going to need postgresql_test.cpp, as we do with sqlite_test.cpp.
        //       It also seems to fail for MySQL as well. -- lexicalunit

        // Check we can iterate over any columns
        {
            nanodbc::catalog::columns columns = catalog.find_columns();
{'fraction_non_alphanumeric': 0.09309871458879321, 'fraction_numerical': 0.005990265818045676, 'mean_word_length': 2.438009438009438, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3067758', 'n_tokens_mistral': 2593, 'n_tokens_neox': 2479, 'n_words': 556}