boostorg / nowide

@@ -16,6 +16,7 @@
Loading
16 16
#include <boost/nowide/utf/utf.hpp>
17 17
#include "test.hpp"
18 18
#include <algorithm>
19 +
#include <cassert>
19 20
#include <cstdlib>
20 21
#include <cstring>
21 22
#include <iostream>
@@ -24,12 +25,8 @@
Loading
24 25
25 26
bool is_ascii(const std::string& s)
26 27
{
27 -
    for(std::string::const_iterator it = s.begin(); it != s.end(); ++it)
28 -
    {
29 -
        if(static_cast<unsigned char>(*it) > 0x7F)
30 -
            return false;
31 -
    }
32 -
    return true;
28 +
    return std::find_if(s.begin(), s.end(), [](const char c) { return static_cast<unsigned char>(c) > 0x7F; })
29 +
           == s.end();
33 30
}
34 31
35 32
std::string replace_non_ascii(const std::string& s)
@@ -84,15 +81,11 @@
Loading
84 81
        std::string key = std::string(key_begin, key_end);
85 82
        const char* std_value = std::getenv(key.c_str());
86 83
        const char* bnw_value = boost::nowide::getenv(key.c_str());
87 -
        // If std_value is set, bnw value must be too and be equal, else bnw value must be unset too
88 -
        if(std_value)
89 -
        {
90 -
            TEST(bnw_value);
91 -
            // Compare only if ascii
92 -
            if(is_ascii(std_value) && std::string(std_value) != std::string(bnw_value))
93 -
                TEST_EQ(std_value, replace_non_ascii(bnw_value));
94 -
        } else
95 -
            TEST(!bnw_value);
84 +
        assert(std_value);
85 +
        TEST(bnw_value);
86 +
        // Compare only if ascii
87 +
        if(is_ascii(std_value) && std::string(std_value) != std::string(bnw_value))
88 +
            TEST_EQ(std_value, replace_non_ascii(bnw_value));
96 89
    }
97 90
}
98 91
@@ -101,14 +94,14 @@
Loading
101 94
void run_child(int argc, char** argv, char** env)
102 95
{
103 96
    // Test arguments
104 -
    TEST(argc == 2);
97 +
    TEST_EQ(argc, 2);
105 98
    TEST_EQ(argv[1], example);
106 -
    TEST(argv[2] == 0);
99 +
    TEST(argv[2] == NULL);
107 100
108 101
    // Test getenv
109 102
    TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST"));
110 103
    TEST_EQ(boost::nowide::getenv("BOOST_NOWIDE_TEST"), example);
111 -
    TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST_NONE") == 0);
104 +
    TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST_NONE") == NULL);
112 105
    // Empty variables are unreliable on windows, hence skip. E.g. using "set FOO=" unsets FOO
113 106
#ifndef BOOST_WINDOWS
114 107
    TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"));
@@ -130,20 +123,21 @@
Loading
130 123
131 124
void run_parent(const char* exe_path)
132 125
{
126 +
    TEST(boost::nowide::system(nullptr) != 0);
133 127
    const std::string command = "\"" + std::string(exe_path) + "\" " + example;
134 128
#if BOOST_NOWIDE_TEST_USE_NARROW
135 -
    TEST(boost::nowide::setenv("BOOST_NOWIDE_TEST", example.c_str(), 1) == 0);
136 -
    TEST(boost::nowide::setenv("BOOST_NOWIDE_TEST_NONE", example.c_str(), 1) == 0);
137 -
    TEST(boost::nowide::unsetenv("BOOST_NOWIDE_TEST_NONE") == 0);
138 -
    TEST(boost::nowide::setenv("BOOST_NOWIDE_EMPTY", "", 1) == 0);
129 +
    TEST_EQ(boost::nowide::setenv("BOOST_NOWIDE_TEST", example.c_str(), 1), 0);
130 +
    TEST_EQ(boost::nowide::setenv("BOOST_NOWIDE_TEST_NONE", example.c_str(), 1), 0);
131 +
    TEST_EQ(boost::nowide::unsetenv("BOOST_NOWIDE_TEST_NONE"), 0);
132 +
    TEST_EQ(boost::nowide::setenv("BOOST_NOWIDE_EMPTY", "", 1), 0);
139 133
    TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"));
140 -
    TEST(boost::nowide::system(command.c_str()) == 0);
134 +
    TEST_EQ(boost::nowide::system(command.c_str()), 0);
141 135
    std::cout << "Parent ok" << std::endl;
142 136
#else
143 137
    const std::wstring envVar = L"BOOST_NOWIDE_TEST=" + boost::nowide::widen(example);
144 -
    TEST(_wputenv(envVar.c_str()) == 0);
138 +
    TEST_EQ(_wputenv(envVar.c_str()), 0);
145 139
    const std::wstring wcommand = boost::nowide::widen(command);
146 -
    TEST(_wsystem(wcommand.c_str()) == 0);
140 +
    TEST_EQ(_wsystem(wcommand.c_str()), 0);
147 141
    std::cout << "Wide Parent ok" << std::endl;
148 142
#endif
149 143
}
@@ -156,7 +150,7 @@
Loading
156 150
    char** old_env = env;
157 151
    {
158 152
        boost::nowide::args _(argc, argv, env);
159 -
        TEST(argc == old_argc);
153 +
        TEST_EQ(argc, old_argc);
160 154
        std::cout << "Checking arguments" << std::endl;
161 155
        compare_string_arrays(old_argv, argv, false);
162 156
        std::cout << "Checking env" << std::endl;
@@ -164,7 +158,7 @@
Loading
164 158
        compare_getenv(env);
165 159
    }
166 160
    // When `args` is destructed the old values must be restored
167 -
    TEST(argc == old_argc);
161 +
    TEST_EQ(argc, old_argc);
168 162
    TEST(argv == old_argv);
169 163
    TEST(env == old_env);
170 164

@@ -25,7 +25,7 @@
Loading
25 25
        TEST(f);
26 26
    }
27 27
    TEST(file_exists(filename));
28 -
    TEST(read_file(filename) == "");
28 +
    TEST_EQ(read_file(filename), "");
29 29
30 30
    // Clear file if exists
31 31
    create_file(filename, "test");
@@ -34,14 +34,14 @@
Loading
34 34
        nw::ofstream f(filename);
35 35
        TEST(f);
36 36
    }
37 -
    TEST(read_file(filename) == "");
37 +
    TEST_EQ(read_file(filename), "");
38 38
39 39
    // At end
40 40
    {
41 41
        nw::ofstream f(filename, std::ios::ate);
42 42
        TEST(f);
43 43
    }
44 -
    TEST(read_file(filename) == "");
44 +
    TEST_EQ(read_file(filename), "");
45 45
46 46
    // Binary mode
47 47
    {
@@ -49,7 +49,7 @@
Loading
49 49
        TEST(f);
50 50
        TEST(f << "test\r\n");
51 51
    }
52 -
    TEST(read_file(filename, data_type::binary) == "test\r\n");
52 +
    TEST_EQ(read_file(filename, data_type::binary), "test\r\n");
53 53
}
54 54
55 55
template<typename T>
@@ -63,7 +63,7 @@
Loading
63 63
        TEST(f);
64 64
    }
65 65
    TEST(file_exists(filename));
66 -
    TEST(read_file(filename) == "");
66 +
    TEST_EQ(read_file(filename), "");
67 67
68 68
    // Clear file if exists
69 69
    create_file(filename, "test");
@@ -73,7 +73,7 @@
Loading
73 73
        f.open(filename);
74 74
        TEST(f);
75 75
    }
76 -
    TEST(read_file(filename) == "");
76 +
    TEST_EQ(read_file(filename), "");
77 77
78 78
    // At end
79 79
    {
@@ -81,7 +81,7 @@
Loading
81 81
        f.open(filename, std::ios::ate);
82 82
        TEST(f);
83 83
    }
84 -
    TEST(read_file(filename) == "");
84 +
    TEST_EQ(read_file(filename), "");
85 85
86 86
    // Binary mode
87 87
    {
@@ -90,14 +90,14 @@
Loading
90 90
        TEST(f);
91 91
        TEST(f << "test\r\n");
92 92
    }
93 -
    TEST(read_file(filename, data_type::binary) == "test\r\n");
93 +
    TEST_EQ(read_file(filename, data_type::binary), "test\r\n");
94 94
}
95 95
96 96
void test_move_and_swap(const std::string& filename)
97 97
{
98 98
    const std::string filename2 = filename + ".2";
99 99
    remove_file_at_exit _(filename);
100 -
    remove_file_at_exit _2(filename);
100 +
    remove_file_at_exit _2(filename2);
101 101
102 102
    // Move construct
103 103
    {
@@ -119,8 +119,8 @@
Loading
119 119
        TEST(f_new);
120 120
        TEST(f_new << "World");
121 121
    }
122 -
    TEST(read_file(filename) == "Hello World");
123 -
    TEST(read_file(filename2) == "Foo");
122 +
    TEST_EQ(read_file(filename), "Hello World");
123 +
    TEST_EQ(read_file(filename2), "Foo");
124 124
125 125
    // Move assign
126 126
    {
@@ -145,8 +145,8 @@
Loading
145 145
        TEST(f_new);
146 146
        TEST(f_new << "World");
147 147
    }
148 -
    TEST(read_file(filename) == "Hello World");
149 -
    TEST(read_file(filename2) == "Foo");
148 +
    TEST_EQ(read_file(filename), "Hello World");
149 +
    TEST_EQ(read_file(filename2), "Foo");
150 150
151 151
    // Swap
152 152
    {
@@ -166,8 +166,8 @@
Loading
166 166
        TEST(!f_old.is_open());
167 167
        TEST(f_new.is_open());
168 168
    }
169 -
    TEST(read_file(filename) == "Hello World");
170 -
    TEST(read_file(filename2) == "Foo Bar");
169 +
    TEST_EQ(read_file(filename), "Hello World");
170 +
    TEST_EQ(read_file(filename2), "Foo Bar");
171 171
}
172 172
173 173
// coverity [root_function]

@@ -14,30 +14,18 @@
Loading
14 14
#include <windows.h>
15 15
#endif
16 16
17 -
// "Safe" strcpy version with NULL termination to make MSVC runtime happy
18 -
// which warns when using strncpy
19 -
template<size_t size>
20 -
void strcpy_safe(char (&dest)[size], const char* src)
21 -
{
22 -
    size_t len = std::strlen(src);
23 -
    if(len >= size)
24 -
        len = size - 1u;
25 -
    std::memcpy(dest, src, len);
26 -
    dest[len] = 0;
27 -
}
28 -
29 17
// coverity [root_function]
30 18
void test_main(int, char**, char**)
31 19
{
32 20
    std::string example = "\xd7\xa9-\xd0\xbc-\xce\xbd";
33 -
    char penv[256] = {0};
34 -
    strcpy_safe(penv, ("BOOST_TEST2=" + example + "x").c_str());
21 +
    std::string envVar = "BOOST_TEST2=" + example + "x";
35 22
36 23
    TEST(boost::nowide::setenv("BOOST_TEST1", example.c_str(), 1) == 0);
37 24
    TEST(boost::nowide::getenv("BOOST_TEST1"));
38 25
    TEST(boost::nowide::getenv("BOOST_TEST1") == example);
39 26
    TEST(boost::nowide::setenv("BOOST_TEST1", "xx", 0) == 0);
40 27
    TEST(boost::nowide::getenv("BOOST_TEST1") == example);
28 +
    char* penv = const_cast<char*>(envVar.c_str());
41 29
    TEST(boost::nowide::putenv(penv) == 0);
42 30
    TEST(boost::nowide::getenv("BOOST_TEST2"));
43 31
    TEST(boost::nowide::getenv("BOOST_TEST_INVALID") == 0);
@@ -45,11 +33,10 @@
Loading
45 33
#ifdef BOOST_WINDOWS
46 34
    // Passing a variable without an equals sign (before \0) is an error
47 35
    // But GLIBC has an extension that unsets the env var instead
48 -
    char penv2[256] = {0};
49 -
    const char* sPenv2 = "BOOST_TEST1SOMEGARBAGE=";
50 -
    strcpy_safe(penv2, sPenv2);
36 +
    std::string envVar2 = "BOOST_TEST1SOMEGARBAGE=";
51 37
    // End the string before the equals sign -> Expect fail
52 -
    penv2[strlen("BOOST_TEST1")] = '\0';
38 +
    envVar2[strlen("BOOST_TEST1")] = '\0';
39 +
    char* penv2 = const_cast<char*>(envVar2.c_str());
53 40
    TEST(boost::nowide::putenv(penv2) == -1);
54 41
    TEST(boost::nowide::getenv("BOOST_TEST1"));
55 42
    TEST(boost::nowide::getenv("BOOST_TEST1") == example);

@@ -165,10 +165,10 @@
Loading
165 165
        TEST(buf[9] == 1);
166 166
        TEST(boost::nowide::narrow(buf, 8, b, e) == 0);
167 167
        TEST(boost::nowide::narrow(buf, 7, b, e - 1) == buf);
168 -
        TEST(buf == hello.substr(0, 6));
168 +
        TEST_EQ(buf, hello.substr(0, 6));
169 169
170 170
        // Raw literals are also possible
171 -
        TEST(boost::nowide::narrow(L"\u05e9\u05dc\u05d5\u05dd") == hello);
171 +
        TEST_EQ(boost::nowide::narrow(L"\u05e9\u05dc\u05d5\u05dd"), hello);
172 172
    }
173 173
174 174
    std::cout << "- boost::nowide::utf::convert_buffer" << std::endl;
@@ -187,6 +187,13 @@
Loading
187 187
        TEST(std::wstring(buf.data()) == whello);
188 188
    }
189 189
190 +
    namespace utf = boost::nowide::utf;
191 +
    // Decode of empty range yields incomplete
192 +
    auto helloIt = hello.begin();
193 +
    TEST_EQ(utf::utf_traits<char>::decode(helloIt, helloIt), utf::incomplete);
194 +
    TEST_EQ(utf::utf_traits<char16_t>::decode(helloIt, helloIt), utf::incomplete);
195 +
    TEST_EQ(utf::utf_traits<char32_t>::decode(helloIt, helloIt), utf::incomplete);
196 +
190 197
    std::cout << "- (output_buffer, buffer_size, input_raw_string)" << std::endl;
191 198
    run_all(widen_buf_ptr, narrow_buf_ptr);
192 199
    std::cout << "- (output_buffer, buffer_size, input_raw_string, string_len)" << std::endl;

@@ -17,12 +17,110 @@
Loading
17 17
#include <locale>
18 18
#include <vector>
19 19
20 +
// MSVC has problems with an undefined symbol std::codecvt::id in some versions if the utf char types are used. See
21 +
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error?forum=vcgeneral
22 +
// Workaround: use int16_t instead of char16_t
23 +
#if defined(_MSC_VER) && _MSC_VER >= 1900 && _MSC_VER <= 1916
24 +
#define BOOST_NOWIDE_REQUIRE_UTF_CHAR_WORKAROUND 1
25 +
#else
26 +
#define BOOST_NOWIDE_REQUIRE_UTF_CHAR_WORKAROUND 0
27 +
#endif
28 +
29 +
#if defined(_MSC_VER) && !BOOST_NOWIDE_REQUIRE_UTF_CHAR_WORKAROUND
30 +
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION 1
31 +
#else
32 +
#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION 0
33 +
#endif
34 +
20 35
static const char* utf8_name =
21 36
  "\xf0\x9d\x92\x9e-\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82-\xE3\x82\x84\xE3\x81\x82.txt";
22 37
static const std::wstring wide_name_str = boost::nowide::widen(utf8_name);
23 38
static const wchar_t* wide_name = wide_name_str.c_str();
24 39
25 40
using cvt_type = std::codecvt<wchar_t, char, std::mbstate_t>;
41 +
#if BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION
42 +
#pragma warning(push)
43 +
#pragma warning(disable : 4996) // Disable deprecation warning for std::codecvt<char16/32_t, char, ...>
44 +
#endif
45 +
#if BOOST_NOWIDE_REQUIRE_UTF_CHAR_WORKAROUND
46 +
using utf16_char_t = int16_t;
47 +
using utf32_char_t = int32_t;
48 +
#else
49 +
using utf16_char_t = char16_t;
50 +
using utf32_char_t = char32_t;
51 +
#endif
52 +
53 +
using cvt_type16 = std::codecvt<utf16_char_t, char, std::mbstate_t>;
54 +
using cvt_type32 = std::codecvt<utf32_char_t, char, std::mbstate_t>;
55 +
#if BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION
56 +
#pragma warning(pop)
57 +
#endif
58 +
using utf8_utf16_codecvt = boost::nowide::utf8_codecvt<utf16_char_t>;
59 +
using utf8_utf32_codecvt = boost::nowide::utf8_codecvt<utf32_char_t>;
60 +
61 +
void test_codecvt_basic()
62 +
{
63 +
    // UTF-16
64 +
    {
65 +
#if BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION
66 +
#pragma warning(push)
67 +
#pragma warning(disable : 4996) // Disable deprecation warning for std::codecvt<char16, char, ...>
68 +
#endif
69 +
        std::locale l(std::locale::classic(), new utf8_utf16_codecvt());
70 +
        const cvt_type16& cvt = std::use_facet<cvt_type16>(l);
71 +
#if BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION
72 +
#pragma warning(pop)
73 +
#endif
74 +
        TEST_EQ(cvt.encoding(), 0);   // Characters have a variable width
75 +
        TEST_EQ(cvt.max_length(), 4); // At most 4 UTF-8 code units are one internal char (one or two UTF-16 code units)
76 +
        TEST(!cvt.always_noconv());   // Always convert
77 +
    }
78 +
    // UTF-32
79 +
    {
80 +
#if BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION
81 +
#pragma warning(push)
82 +
#pragma warning(disable : 4996) // Disable deprecation warning for std::codecvt<char32, char, ...>
83 +
#endif
84 +
        std::locale l(std::locale::classic(), new utf8_utf32_codecvt());
85 +
        const cvt_type32& cvt = std::use_facet<cvt_type32>(l);
86 +
#if BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION
87 +
#pragma warning(pop)
88 +
#endif
89 +
        TEST_EQ(cvt.encoding(), 0);   // Characters have a variable width
90 +
        TEST_EQ(cvt.max_length(), 4); // At most 4 UTF-8 code units are one internal char (one UTF-32 code unit)
91 +
        TEST(!cvt.always_noconv());   // Always convert
92 +
    }
93 +
}
94 +
95 +
void test_codecvt_unshift()
96 +
{
97 +
    char buf[256];
98 +
    const auto name16 = boost::nowide::utf::convert_string<utf16_char_t>(utf8_name, utf8_name + std::strlen(utf8_name));
99 +
100 +
    utf8_utf16_codecvt cvt16;
101 +
    {
102 +
        const cvt_type16& cvt = cvt16;
103 +
        // Unshift on initial state does nothing
104 +
        std::mbstate_t mb{};
105 +
        char* to_next;
106 +
#if BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION
107 +
#pragma warning(push)
108 +
#pragma warning(disable : 4996) // Disable deprecation warning for std::codecvt<char16, char, ...>
109 +
#endif
110 +
        TEST_EQ(cvt.unshift(mb, buf, std::end(buf), to_next), cvt_type16::ok);
111 +
        TEST(to_next == buf);
112 +
        const utf16_char_t* from_next;
113 +
        // Convert into a to small buffer
114 +
        TEST_EQ(cvt.out(mb, &name16.front(), &name16.back(), from_next, buf, buf + 1, to_next), cvt_type16::partial);
115 +
        TEST(from_next == &name16[1]);
116 +
        TEST(to_next == buf);
117 +
        // Unshift on non-default state is not possible
118 +
        TEST_EQ(cvt.unshift(mb, buf, std::end(buf), to_next), cvt_type16::error);
119 +
#if BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION
120 +
#pragma warning(pop)
121 +
#endif
122 +
    }
123 +
}
26 124
27 125
void test_codecvt_in_n_m(const cvt_type& cvt, size_t n, size_t m)
28 126
{
@@ -52,8 +150,8 @@
Loading
52 150
        std::codecvt_base::result r = cvt.in(mb, from, from_end, from_next, to, to_end, to_next);
53 151
54 152
        int count = cvt.length(mb2, from, from_end, to_end - to);
55 -
        TEST(std::memcmp(&mb, &mb2, sizeof(mb)) == 0);
56 -
        TEST(count == from_next - from);
153 +
        TEST_EQ(std::memcmp(&mb, &mb2, sizeof(mb)), 0);
154 +
        TEST_EQ(count, from_next - from);
57 155
58 156
        if(r == cvt_type::partial)
59 157
        {
@@ -61,7 +159,7 @@
Loading
61 159
            if(from_end > real_end)
62 160
                from_end = real_end;
63 161
        } else
64 -
            TEST(r == cvt_type::ok);
162 +
            TEST_EQ(r, cvt_type::ok);
65 163
        while(to != to_next)
66 164
        {
67 165
            TEST(*wptr == *to);
@@ -112,17 +210,16 @@
Loading
112 210
            {
113 211
                TEST(to_end - to_next < cvt.max_length());
114 212
                to_end += n;
115 -
                if(to_end > real_to_end)
116 -
                    to_end = real_to_end;
213 +
                TEST(to_end <= real_to_end); // Should always be big enough
117 214
            }
118 215
        } else
119 216
        {
120 -
            TEST(r == cvt_type::ok);
217 +
            TEST_EQ(r, cvt_type::ok);
121 218
        }
122 219
123 220
        while(to != to_next)
124 221
        {
125 -
            TEST(*nptr == *to);
222 +
            TEST_EQ(*nptr, *to);
126 223
            nptr++;
127 224
            to++;
128 225
        }
@@ -130,7 +227,7 @@
Loading
130 227
    }
131 228
    TEST(nptr == utf8_name + u8len);
132 229
    TEST(from_next == real_from_end);
133 -
    TEST(cvt.unshift(mb, to, to + n, to_next) == cvt_type::ok);
230 +
    TEST_EQ(cvt.unshift(mb, to, to + n, to_next), cvt_type::ok);
134 231
    TEST(to_next == to);
135 232
}
136 233
@@ -151,10 +248,12 @@
Loading
151 248
            {
152 249
                test_codecvt_in_n_m(cvt, i, j);
153 250
                test_codecvt_out_n_m(cvt, i, j);
154 -
            } catch(...)
251 +
            } catch(...) // LCOV_EXCL_LINE
155 252
            {
253 +
                // LCOV_EXCL_START
156 254
                std::cerr << "Wlen=" << j << " Nlen=" << i << std::endl;
157 255
                throw;
256 +
                // LCOV_EXCL_STOP
158 257
            }
159 258
        }
160 259
    }
@@ -179,7 +278,7 @@
Loading
179 278
            const char* from_end = from + std::strlen(from);
180 279
            const char* from_next = from;
181 280
            wchar_t* to_next = to;
182 -
            TEST(cvt.in(mb, from, from_end, from_next, to, to_end, to_next) == cvt_type::ok);
281 +
            TEST_EQ(cvt.in(mb, from, from_end, from_next, to, to_end, to_next), cvt_type::ok);
183 282
            TEST(from_next == from + 5);
184 283
            TEST(to_next == to + 4);
185 284
            TEST(std::wstring(to, to_end) == boost::nowide::widen(err_utf));
@@ -194,7 +293,7 @@
Loading
194 293
            const char* from_end = from + std::strlen(from);
195 294
            const char* from_next = from;
196 295
            wchar_t* to_next = to;
197 -
            TEST(cvt.in(mb, from, from_end, from_next, to, to_end, to_next) == cvt_type::partial);
296 +
            TEST_EQ(cvt.in(mb, from, from_end, from_next, to, to_end, to_next), cvt_type::partial);
198 297
            TEST(from_next == from + 1);
199 298
            TEST(to_next == to + 1);
200 299
            TEST(std::wstring(to, to_next) == std::wstring(L"1"));
@@ -215,17 +314,17 @@
Loading
215 314
#endif
216 315
            if(sizeof(wchar_t) == 2)
217 316
            {
218 -
                TEST(res == cvt_type::partial);
317 +
                TEST_EQ(res, cvt_type::partial);
219 318
                TEST(from_next == from_end);
220 319
                TEST(to_next == to);
221 -
                TEST(buf[0] == 0);
320 +
                TEST_EQ(buf[0], 0);
222 321
            } else
223 322
            {
224 -
                TEST(res == cvt_type::ok);
323 +
                TEST_EQ(res, cvt_type::ok);
225 324
                TEST(from_next == from_end);
226 325
                TEST(to_next == to + 3);
227 326
                // surrogate is invalid
228 -
                TEST(std::string(to, to_next) == boost::nowide::narrow(wreplacement_str));
327 +
                TEST_EQ(std::string(to, to_next), boost::nowide::narrow(wreplacement_str));
229 328
            }
230 329
        }
231 330
    }
@@ -243,10 +342,10 @@
Loading
243 342
            const wchar_t* from = err_utf;
244 343
            const wchar_t* from_end = from + std::wcslen(from);
245 344
            const wchar_t* from_next = from;
246 -
            TEST(cvt.out(mb, from, from_end, from_next, to, to_end, to_next) == cvt_type::ok);
345 +
            TEST_EQ(cvt.out(mb, from, from_end, from_next, to, to_end, to_next), cvt_type::ok);
247 346
            TEST(from_next == from + 2);
248 347
            TEST(to_next == to + 4);
249 -
            TEST(std::string(to, to_next) == "1" + boost::nowide::narrow(wreplacement_str));
348 +
            TEST_EQ(std::string(to, to_next), "1" + boost::nowide::narrow(wreplacement_str));
250 349
        }
251 350
    }
252 351
}
@@ -267,13 +366,15 @@
Loading
267 366
    wchar_t* const to_end = to + buf.size();
268 367
    wchar_t* to_next = to;
269 368
369 +
    const auto expected_consumed = cvt.length(mb, from, from_end, buf.size());
270 370
    cvt_type::result res = cvt.in(mb, from, from_end, from_next, to, to_end, to_next);
371 +
    TEST_EQ(expected_consumed, from_next - from);
271 372
    if(res == cvt_type::partial)
272 373
    {
273 374
        TEST(to_next < to_end);
274 375
        *(to_next++) = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
275 376
    } else
276 -
        TEST(res == cvt_type::ok);
377 +
        TEST_EQ(res, cvt_type::ok);
277 378
278 379
    return std::wstring(to, to_next);
279 380
}
@@ -300,7 +401,7 @@
Loading
300 401
        TEST(to_next < to_end);
301 402
        return std::string(to, to_next) + boost::nowide::narrow(wreplacement_str);
302 403
    } else
303 -
        TEST(res == cvt_type::ok);
404 +
        TEST_EQ(res, cvt_type::ok);
304 405
305 406
    return std::string(to, to_next);
306 407
}
@@ -314,6 +415,8 @@
Loading
314 415
// coverity [root_function]
315 416
void test_main(int, char**, char**)
316 417
{
418 +
    test_codecvt_basic();
419 +
    test_codecvt_unshift();
317 420
    test_codecvt_conv();
318 421
    test_codecvt_err();
319 422
    test_codecvt_subst();

@@ -153,7 +153,7 @@
Loading
153 153
        char c = i % 96 + ' ';
154 154
        TEST(nw::cin.putback(c));
155 155
        int ci = i % 96 + ' ';
156 -
        TEST(nw::cin.get() == ci);
156 +
        TEST_EQ(nw::cin.get(), ci);
157 157
    }
158 158
159 159
    INSTALL_MOCK_BUF(cin, mock_input_buffer);
@@ -172,7 +172,7 @@
Loading
172 172
            for(int i = num_putback_chars - 1; i >= 0; i--)
173 173
            {
174 174
                const int c = getChar(i);
175 -
                TEST(nw::cin.get() == c);
175 +
                TEST_EQ(nw::cin.get(), c);
176 176
            }
177 177
            // Check unget (all chars)
178 178
            for(int i = 0; i < num_putback_chars; i++)
@@ -182,7 +182,7 @@
Loading
182 182
            for(int i = num_putback_chars - 1; i >= 0; i--)
183 183
            {
184 184
                const int c = getChar(i);
185 -
                TEST(nw::cin.get() == c);
185 +
                TEST_EQ(nw::cin.get(), c);
186 186
            }
187 187
        }
188 188
#ifndef BOOST_NOWIDE_TEST_INTERACTIVE
@@ -191,7 +191,7 @@
Loading
191 191
        mock_buf.inputs.push(L"est\r\n");
192 192
        std::string test;
193 193
        TEST(nw::cin >> test);
194 -
        TEST(test == "Test");
194 +
        TEST_EQ(test, "Test");
195 195
#endif
196 196
    }
197 197
}
@@ -319,7 +319,7 @@
Loading
319 319
        input.append(L"\U0010FFFF");
320 320
    mock_buf.inputs.push(input + L"\r\n");
321 321
    TEST(std::getline(nw::cin, value));
322 -
    TEST(value == nw::narrow(input));
322 +
    TEST_EQ(value, nw::narrow(input));
323 323
#endif
324 324
}
325 325
@@ -354,7 +354,7 @@
Loading
354 354
        const std::string expected = create_random_one_line_string(i) + "\x1a";
355 355
        mock_buf.inputs.push(std::wstring(expected.begin(), expected.end()) + L"\r\n");
356 356
        TEST(std::getline(nw::cin, value));
357 -
        TEST(value == expected);
357 +
        TEST_EQ(value, expected);
358 358
    }
359 359
#endif
360 360
}
@@ -362,6 +362,7 @@
Loading
362 362
// coverity [root_function]
363 363
void test_main(int argc, char** argv, char**)
364 364
{
365 +
    // LCOV_EXCL_START
365 366
    if(usesNowideRdBufIn)
366 367
        nw::cout << "Using Nowide input buffer\n";
367 368
    else
@@ -370,6 +371,7 @@
Loading
370 371
        nw::cout << "Using Nowide output buffer\n";
371 372
    else
372 373
        nw::cout << "NOT using Nowide output buffer\n";
374 +
    // LCOV_EXCL_STOP
373 375
374 376
    const std::string arg = (argc == 1) ? "" : argv[1];
375 377
    if(arg == "passthrough") // Read string from cin and write to cout

@@ -46,7 +46,7 @@
Loading
46 46
#if BOOST_MSVC
47 47
#include <crtdbg.h> // For _CrtSetReportMode
48 48
void noop_invalid_param_handler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned, uintptr_t)
49 -
{}
49 +
{} // LCOV_EXCL_LINE
50 50
#endif
51 51
52 52
// coverity [root_function]

@@ -213,7 +213,10 @@
Loading
213 213
            setg(NULL, NULL, NULL);
214 214
            setp(NULL, NULL);
215 215
            if(owns_buffer_)
216 +
            {
216 217
                delete[] buffer_;
218 +
                owns_buffer_ = false;
219 +
            }
217 220
            buffer_ = s;
218 221
            buffer_size_ = (n >= 0) ? static_cast<size_t>(n) : 0;
219 222
            return this;

@@ -13,175 +13,288 @@
Loading
13 13
#include "test.hpp"
14 14
#include <fstream>
15 15
#include <iostream>
16 +
#include <locale>
16 17
#include <string>
17 18
18 19
namespace nw = boost::nowide;
19 20
using namespace boost::nowide::test;
20 21
22 +
class dummyCvtConverting : public std::codecvt<char, char, std::mbstate_t>
23 +
{
24 +
protected:
25 +
    bool do_always_noconv() const noexcept override
26 +
    {
27 +
        return false;
28 +
    }
29 +
};
30 +
31 +
class dummyCvtNonConverting : public std::codecvt<char, char, std::mbstate_t>
32 +
{
33 +
protected:
34 +
    bool do_always_noconv() const noexcept override
35 +
    {
36 +
        return true;
37 +
    }
38 +
};
39 +
40 +
std::string get_narrow_name(const std::string& name)
41 +
{
42 +
    return name;
43 +
}
44 +
std::string get_narrow_name(const std::wstring& name)
45 +
{
46 +
    return boost::nowide::narrow(name);
47 +
}
48 +
21 49
template<class T>
22 50
void test_ctor(const T& filename)
23 51
{
24 -
    remove_file_at_exit _(filename);
52 +
    const std::string narrow_filename = get_narrow_name(filename);
53 +
    remove_file_at_exit _(narrow_filename);
25 54
26 55
    // Fail on non-existing file
27 56
    {
28 -
        ensure_not_exists(filename);
57 +
        ensure_not_exists(narrow_filename);
29 58
        nw::fstream f(filename);
30 59
        TEST(!f);
31 60
    }
32 -
    TEST(!file_exists(filename));
61 +
    TEST(!file_exists(narrow_filename));
33 62
34 63
    // Create empty file
35 64
    {
36 -
        ensure_not_exists(filename);
65 +
        ensure_not_exists(narrow_filename);
37 66
        nw::fstream f(filename, std::ios::out);
38 67
        TEST(f);
39 68
    }
40 -
    TEST(read_file(filename).empty());
69 +
    TEST(read_file(narrow_filename).empty());
41 70
42 71
    // Read+write existing file
43 -
    create_file(filename, "Hello");
72 +
    create_file(narrow_filename, "Hello");
44 73
    {
45 74
        nw::fstream f(filename);
75 +
        TEST(f);
46 76
        std::string tmp;
47 77
        TEST(f >> tmp);
48 78
        TEST(f.eof());
49 -
        TEST(tmp == "Hello");
79 +
        TEST_EQ(tmp, "Hello");
50 80
        f.clear();
51 81
        TEST(f << "World");
52 82
    }
53 -
    TEST(read_file(filename) == "HelloWorld");
54 -
    create_file(filename, "Hello");
83 +
    TEST_EQ(read_file(narrow_filename), "HelloWorld");
84 +
    create_file(narrow_filename, "Hello");
55 85
    {
56 86
        nw::fstream f(filename, std::ios::out | std::ios::in);
87 +
        TEST(f);
57 88
        std::string tmp;
58 89
        TEST(f >> tmp);
59 90
        TEST(f.eof());
60 -
        TEST(tmp == "Hello");
91 +
        TEST_EQ(tmp, "Hello");
61 92
        f.clear();
62 93
        TEST(f << "World");
63 94
    }
64 -
    TEST(read_file(filename) == "HelloWorld");
95 +
    TEST_EQ(read_file(narrow_filename), "HelloWorld");
65 96
66 97
    // Readonly existing file
67 -
    create_file(filename, "Hello");
98 +
    create_file(narrow_filename, "Hello");
68 99
    {
69 100
        nw::fstream f(filename, std::ios::in);
101 +
        TEST(f);
70 102
        std::string tmp;
71 103
        TEST(f >> tmp);
72 -
        TEST(tmp == "Hello");
104 +
        TEST_EQ(tmp, "Hello");
73 105
        f.clear();
74 106
        TEST(f);
75 107
        TEST(!(f << "World"));
76 108
    }
77 -
    TEST(read_file(filename) == "Hello");
109 +
    TEST_EQ(read_file(narrow_filename), "Hello");
78 110
79 111
    // Write existing file
80 -
    create_file(filename, "Hello");
112 +
    create_file(narrow_filename, "Hello");
81 113
    {
82 114
        nw::fstream f(filename, std::ios::out);
83 -
        std::string tmp;
84 115
        TEST(f);
116 +
        std::string tmp;
85 117
        TEST(!(f >> tmp));
86 118
        f.clear();
87 119
        TEST(f << "World");
88 120
    }
89 -
    TEST(read_file(filename) == "World");
121 +
    TEST_EQ(read_file(narrow_filename), "World");
90 122
    // Write existing file with explicit trunc
91 -
    create_file(filename, "Hello");
123 +
    create_file(narrow_filename, "Hello");
92 124
    {
93 125
        nw::fstream f(filename, std::ios::out | std::ios::trunc);
94 -
        std::string tmp;
95 126
        TEST(f);
127 +
        std::string tmp;
96 128
        TEST(!(f >> tmp));
97 129
        f.clear();
98 130
        TEST(f << "World");
99 131
    }
100 -
    TEST(read_file(filename) == "World");
132 +
    TEST_EQ(read_file(narrow_filename), "World");
101 133
102 134
    // append existing file
103 -
    create_file(filename, "Hello");
135 +
    create_file(narrow_filename, "Hello");
104 136
    {
105 137
        nw::fstream f(filename, std::ios::app);
138 +
        TEST(f);
106 139
        TEST(f << "World");
107 140
    }
108 -
    TEST(read_file(filename) == "HelloWorld");
109 -
    create_file(filename, "Hello");
141 +
    TEST_EQ(read_file(narrow_filename), "HelloWorld");
142 +
    create_file(narrow_filename, "Hello");
110 143
    {
111 144
        nw::fstream f(filename, std::ios::out | std::ios::app);
145 +
        TEST(f);
112 146
        TEST(f << "World");
113 147
    }
114 -
    TEST(read_file(filename) == "HelloWorld");
148 +
    TEST_EQ(read_file(narrow_filename), "HelloWorld");
115 149
116 150
    // read+write+truncate existing file
117 -
    create_file(filename, "Hello");
151 +
    create_file(narrow_filename, "Hello");
118 152
    {
119 153
        nw::fstream f(filename, std::ios::out | std::ios::in | std::ios::trunc);
120 -
        std::string tmp;
121 154
        TEST(f);
155 +
        std::string tmp;
122 156
        TEST(!(f >> tmp));
123 157
        f.clear();
124 158
        TEST(f << "World");
125 159
        f.seekg(0);
126 160
        TEST(f >> tmp);
127 -
        TEST(tmp == "World");
161 +
        TEST_EQ(tmp, "World");
128 162
    }
129 -
    TEST(read_file(filename) == "World");
163 +
    TEST_EQ(read_file(narrow_filename), "World");
130 164
131 165
    // read+write+append existing file
132 -
    create_file(filename, "Hello");
166 +
    create_file(narrow_filename, "Hello");
133 167
    {
134 168
        nw::fstream f(filename, std::ios::out | std::ios::in | std::ios::app);
135 169
        TEST(f);
136 170
        TEST(f.seekg(0)); // It is not defined where the read position is after opening
137 -
        TEST(f.tellg() == std::streampos(0));
171 +
        TEST_EQ(f.tellg(), std::streampos(0));
138 172
        std::string tmp;
139 173
        TEST(f >> tmp);
140 -
        TEST(tmp == "Hello");
174 +
        TEST_EQ(tmp, "Hello");
141 175
        f.seekg(0);
142 176
        TEST(f << "World");
143 177
    }
144 -
    TEST(read_file(filename) == "HelloWorld");
145 -
    create_file(filename, "Hello");
178 +
    TEST_EQ(read_file(narrow_filename), "HelloWorld");
179 +
    create_file(narrow_filename, "Hello");
146 180
    {
147 181
        nw::fstream f(filename, std::ios::in | std::ios::app);
182 +
        TEST(f);
148 183
        std::string tmp;
149 184
        TEST(f.seekg(0)); // It is not defined where the read position is after opening
150 -
        TEST(f.tellg() == std::streampos(0));
185 +
        TEST_EQ(f.tellg(), std::streampos(0));
151 186
        TEST(f >> tmp);
152 -
        TEST(tmp == "Hello");
187 +
        TEST_EQ(tmp, "Hello");
153 188
        f.seekg(0);
154 189
        TEST(f << "World");
155 190
    }
156 -
    TEST(read_file(filename) == "HelloWorld");
191 +
    TEST_EQ(read_file(narrow_filename), "HelloWorld");
157 192
158 193
    // Write at end
159 -
    create_file(filename, "Hello");
194 +
    create_file(narrow_filename, "Hello");
160 195
    {
161 196
        nw::fstream f(filename, std::ios::out | std::ios::in | std::ios::ate);
162 -
        std::string tmp;
163 197
        TEST(f);
198 +
        std::string tmp;
164 199
        TEST(!(f >> tmp));
165 200
        f.clear();
166 201
        TEST(f << "World");
167 202
        f.seekg(0);
168 203
        TEST(f >> tmp);
169 -
        TEST(tmp == "HelloWorld");
204 +
        TEST_EQ(tmp, "HelloWorld");
205 +
    }
206 +
    TEST_EQ(read_file(narrow_filename), "HelloWorld");
207 +
208 +
    // binary append existing file
209 +
    create_file(narrow_filename, "Hello");
210 +
    {
211 +
        nw::fstream f(filename, std::ios::binary | std::ios::out | std::ios::app);
212 +
        TEST(f);
213 +
        TEST(f << "World");
214 +
        TEST(f.seekp(0));
215 +
        TEST(f.seekg(0));
216 +
        TEST(f << "World\n");
217 +
    }
218 +
    TEST_EQ(read_file(narrow_filename, data_type::binary), "HelloWorldWorld\n");
219 +
    create_file(narrow_filename, "Hello");
220 +
    {
221 +
        nw::fstream f(filename, std::ios::binary | std::ios::app);
222 +
        TEST(f);
223 +
        TEST(f << "World");
224 +
        TEST(f.seekp(0));
225 +
        TEST(f.seekg(0));
226 +
        TEST(f << "World\n");
170 227
    }
171 -
    TEST(read_file(filename) == "HelloWorld");
228 +
    TEST_EQ(read_file(narrow_filename, data_type::binary), "HelloWorldWorld\n");
229 +
230 +
    // binary out & trunc
231 +
    create_file(narrow_filename, "Hello");
232 +
    {
233 +
        nw::fstream f(filename, std::ios::binary | std::ios::out | std::ios::trunc);
234 +
        TEST(f);
235 +
        TEST(f << "Hello\n");
236 +
        TEST(f << "World");
237 +
    }
238 +
    TEST_EQ(read_file(narrow_filename, data_type::binary), "Hello\nWorld");
239 +
240 +
    // Binary in&out
241 +
    create_file(narrow_filename, "Hello");
242 +
    {
243 +
        nw::fstream f(filename, std::ios::binary | std::ios::out | std::ios::in);
244 +
        TEST(f);
245 +
        std::string tmp;
246 +
        TEST(f >> tmp);
247 +
        TEST(f.eof());
248 +
        TEST_EQ(tmp, "Hello");
249 +
        f.clear();
250 +
        TEST(f << "World\n");
251 +
    }
252 +
    TEST_EQ(read_file(narrow_filename, data_type::binary), "HelloWorld\n");
172 253
173 254
    // Trunc & binary
174 -
    create_file(filename, "Hello");
255 +
    create_file(narrow_filename, "Hello");
175 256
    {
176 -
        nw::fstream f(filename, std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
257 +
        nw::fstream f(filename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc);
177 258
        TEST(f);
178 -
        TEST(f << "test\r\n");
179 -
        std::string tmp(6, '\0');
259 +
        TEST(f << "test\n");
260 +
        std::string tmp(5, '\0');
180 261
        TEST(f.seekg(0));
181 -
        TEST(f.read(&tmp[0], 6));
182 -
        TEST(tmp == "test\r\n");
262 +
        TEST(f.read(&tmp[0], 5));
263 +
        TEST_EQ(tmp, "test\n");
264 +
    }
265 +
    TEST_EQ(read_file(narrow_filename, data_type::binary), "test\n");
266 +
267 +
    // Binary in&out append
268 +
    create_file(narrow_filename, "Hello");
269 +
    {
270 +
        nw::fstream f(filename, std::ios::binary | std::ios::in | std::ios::out | std::ios::app);
271 +
        TEST(f);
272 +
        TEST(f.seekg(0)); // It is not defined where the read position is after opening
273 +
        std::string tmp;
274 +
        TEST(f >> tmp);
275 +
        TEST(f.eof());
276 +
        TEST_EQ(tmp, "Hello");
277 +
        f.clear();
278 +
        f.seekg(0);
279 +
        f.seekp(0);
280 +
        TEST(f << "World\n");
281 +
    }
282 +
    TEST_EQ(read_file(narrow_filename, data_type::binary), "HelloWorld\n");
283 +
    create_file(narrow_filename, "Hello");
284 +
    {
285 +
        nw::fstream f(filename, std::ios::binary | std::ios::in | std::ios::app);
286 +
        TEST(f);
287 +
        TEST(f.seekg(0)); // It is not defined where the read position is after opening
288 +
        std::string tmp;
289 +
        TEST(f >> tmp);
290 +
        TEST(f.eof());
291 +
        TEST_EQ(tmp, "Hello");
292 +
        f.clear();
293 +
        f.seekg(0);
294 +
        f.seekp(0);
295 +
        TEST(f << "World\n");
183 296
    }
184 -
    TEST(read_file(filename, data_type::binary) == "test\r\n");
297 +
    TEST_EQ(read_file(narrow_filename, data_type::binary), "HelloWorld\n");
185 298
186 299
    // Invalid modes
187 300
    const std::initializer_list<std::ios::openmode> invalid_modes{
@@ -196,65 +309,77 @@
Loading
196 309
    };
197 310
    for(const auto mode : invalid_modes)
198 311
    {
199 -
        create_file(filename, "Hello");
312 +
        create_file(narrow_filename, "Hello");
200 313
        {
201 314
            nw::fstream f(filename, mode);
202 315
            TEST(!f);
203 316
        }
204 -
        TEST(read_file(filename) == "Hello");
317 +
        TEST_EQ(read_file(narrow_filename), "Hello");
205 318
    }
206 319
}
207 320
321 +
void test_imbue()
322 +
{
323 +
    boost::nowide::fstream f;
324 +
#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
325 +
    std::locale convLocale(std::locale::classic(), new dummyCvtConverting);
326 +
    TEST_THROW(f.imbue(convLocale), std::runtime_error);
327 +
#endif
328 +
    std::locale nonconvLocale(std::locale::classic(), new dummyCvtNonConverting);
329 +
    f.imbue(nonconvLocale); // No exception, do nothing
330 +
}
331 +
208 332
template<typename T>
209 333
void test_open(const T& filename)
210 334
{
211 -
    remove_file_at_exit _(filename);
335 +
    const std::string narrow_filename = get_narrow_name(filename);
336 +
    remove_file_at_exit _(narrow_filename);
212 337
213 338
    // Fail on non-existing file
214 339
    {
215 -
        ensure_not_exists(filename);
340 +
        ensure_not_exists(narrow_filename);
216 341
        nw::fstream f;
217 342
        f.open(filename);
218 343
        TEST(!f);
219 344
    }
220 -
    TEST(!file_exists(filename));
345 +
    TEST(!file_exists(narrow_filename));
221 346
222 347
    // Create empty file
223 348
    {
224 -
        ensure_not_exists(filename);
349 +
        ensure_not_exists(narrow_filename);
225 350
        nw::fstream f;
226 351
        f.open(filename, std::ios::out);
227 352
        TEST(f);
228 353
    }
229 -
    TEST(read_file(filename).empty());
354 +
    TEST(read_file(narrow_filename).empty());
230 355
231 356
    // Read+write existing file
232 -
    create_file(filename, "Hello");
357 +
    create_file(narrow_filename, "Hello");
233 358
    {
234 359
        nw::fstream f;
235 360
        f.open(filename);
236 361
        std::string tmp;
237 362
        TEST(f >> tmp);
238 363
        TEST(f.eof());
239 -
        TEST(tmp == "Hello");
364 +
        TEST_EQ(tmp, "Hello");
240 365
        f.clear();
241 366
        TEST(f << "World");
242 367
    }
243 -
    TEST(read_file(filename) == "HelloWorld");
368 +
    TEST_EQ(read_file(narrow_filename), "HelloWorld");
244 369
245 370
    // Readonly existing file
246 -
    create_file(filename, "Hello");
371 +
    create_file(narrow_filename, "Hello");
247 372
    {
248 373
        nw::fstream f;
249 374
        f.open(filename, std::ios::in);
250 375
        std::string tmp;
251 376
        TEST(f >> tmp);
252 -
        TEST(tmp == "Hello");
377 +
        TEST_EQ(tmp, "Hello");
253 378
        f.clear();
254 379
        TEST(f);
255 380
        TEST(!(f << "World"));
256 381
    }
257 -
    TEST(read_file(filename) == "Hello");
382 +
    TEST_EQ(read_file(narrow_filename), "Hello");
258 383
259 384
    // remaining mode cases skipped as they are already tested by the ctor tests
260 385
}
@@ -263,7 +388,7 @@
Loading
263 388
bool is_open(T& stream)
264 389
{
265 390
    // There are const and non const versions of is_open, so test both
266 -
    TEST(stream.is_open() == const_cast<const T&>(stream).is_open());
391 +
    TEST_EQ(stream.is_open(), const_cast<const T&>(stream).is_open());
267 392
    return stream.is_open();
268 393
}
269 394
@@ -278,6 +403,9 @@
Loading
278 403
    f.close();
279 404
    TEST(f);
280 405
    TEST(!is_open(f));
406 +
    // Closing again fails
407 +
    f.close();
408 +
    TEST(!f);
281 409
}
282 410
283 411
/// Test is_open for all 3 fstream classes
@@ -295,7 +423,7 @@
Loading
295 423
    const std::string filename2 = filename + ".2";
296 424
    create_file(filename2, "Foo Bar");
297 425
    remove_file_at_exit _(filename);
298 -
    remove_file_at_exit _2(filename);
426 +
    remove_file_at_exit _2(filename2);
299 427
300 428
    // Move construct
301 429
    {
@@ -314,14 +442,14 @@
Loading
314 442
        std::string s;
315 443
        TEST(f_old);
316 444
        TEST(f_old >> s);
317 -
        TEST(s == "Foo");
445 +
        TEST_EQ(s, "Foo");
318 446
319 447
        // new starts where the old was left of
320 448
        TEST(f_new);
321 449
        TEST(f_new << "World");
322 450
    }
323 -
    TEST(read_file(filename) == "Hello World");
324 -
    TEST(read_file(filename2) == "Foo Bar");
451 +
    TEST_EQ(read_file(filename), "Hello World");
452 +
    TEST_EQ(read_file(filename2), "Foo Bar");
325 453
326 454
    // Move assign
327 455
    {
@@ -342,14 +470,14 @@
Loading
342 470
#endif
343 471
            std::string s;
344 472
            TEST(f_old >> s);
345 -
            TEST(s == "ReadThis");
473 +
            TEST_EQ(s, "ReadThis");
346 474
        }
347 475
        // new starts where the old was left of
348 476
        TEST(f_new);
349 477
        TEST(f_new << "World");
350 478
    }
351 -
    TEST(read_file(filename) == "Hello World");
352 -
    TEST(read_file(filename2) == "ReadThis");
479 +
    TEST_EQ(read_file(filename), "Hello World");
480 +
    TEST_EQ(read_file(filename2), "ReadThis");
353 481
354 482
    create_file(filename2, "Foo Bar");
355 483
    // Swap
@@ -360,12 +488,12 @@
Loading
360 488
        nw::fstream f_new(filename2, std::ios::in);
361 489
        std::string s;
362 490
        TEST(f_new >> s);
363 -
        TEST(s == "Foo");
491 +
        TEST_EQ(s, "Foo");
364 492
365 493
        // After swapping both are valid and where they left
366 494
        f_new.swap(f_old);
367 495
        TEST(f_old >> s);
368 -
        TEST(s == "Bar");
496 +
        TEST_EQ(s, "Bar");
369 497
        TEST(f_new << "World");
370 498
371 499
        f_new.close();
@@ -373,8 +501,8 @@
Loading
373 501
        TEST(!f_old.is_open());
374 502
        TEST(f_new.is_open());
375 503
    }
376 -
    TEST(read_file(filename) == "Hello World");
377 -
    TEST(read_file(filename2) == "Foo Bar");
504 +
    TEST_EQ(read_file(filename), "Hello World");
505 +
    TEST_EQ(read_file(filename2), "Foo Bar");
378 506
}
379 507
380 508
void test_flush(const std::string& filepath)
@@ -397,9 +525,12 @@
Loading
397 525
            nw::fstream fi(filepath, std::ios_base::in);
398 526
            TEST(fi >> s);
399 527
            // coverity[tainted_data]
400 -
            TEST(s == curValue);
528 +
            TEST_EQ(s, curValue);
401 529
        }
402 530
    }
531 +
    fo.close();
532 +
    TEST(fo.flush());   // Should also work on closed stream
533 +
    TEST(!fo.seekg(0)); // Does not work on closed stream
403 534
}
404 535
405 536
// coverity [root_function]
@@ -410,14 +541,23 @@
Loading
410 541
    std::cout << "Ctor" << std::endl;
411 542
    test_ctor<const char*>(exampleFilename.c_str());
412 543
    test_ctor<std::string>(exampleFilename);
544 +
#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS
545 +
    test_ctor<const wchar_t*>(boost::nowide::widen(exampleFilename).c_str());
546 +
#endif
413 547
414 548
    std::cout << "Open" << std::endl;
415 549
    test_open<const char*>(exampleFilename.c_str());
416 550
    test_open<std::string>(exampleFilename);
551 +
#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS
552 +
    test_open<const wchar_t*>(boost::nowide::widen(exampleFilename).c_str());
553 +
#endif
417 554
418 555
    std::cout << "IsOpen" << std::endl;
419 556
    test_is_open(exampleFilename);
420 557
558 +
    std::cout << "imbue" << std::endl;
559 +
    test_imbue();
560 +
421 561
    std::cout << "Move and swap" << std::endl;
422 562
    test_move_and_swap(exampleFilename);
423 563

@@ -33,9 +33,9 @@
Loading
33 33
        TEST(f);
34 34
        std::string tmp;
35 35
        TEST(f >> tmp);
36 -
        TEST(tmp == "test");
36 +
        TEST_EQ(tmp, "test");
37 37
    }
38 -
    TEST(read_file(filename) == "test");
38 +
    TEST_EQ(read_file(filename), "test");
39 39
40 40
    // At end
41 41
    {
@@ -47,9 +47,9 @@
Loading
47 47
        f.clear();
48 48
        f.seekg(0, std::ios::beg);
49 49
        TEST(f >> tmp);
50 -
        TEST(tmp == "test");
50 +
        TEST_EQ(tmp, "test");
51 51
    }
52 -
    TEST(read_file(filename) == "test");
52 +
    TEST_EQ(read_file(filename), "test");
53 53
54 54
    create_file(filename, "test\r\n", data_type::binary);
55 55
    // Binary mode
@@ -58,7 +58,7 @@
Loading
58 58
        TEST(f);
59 59
        std::string tmp(6, '\0');
60 60
        TEST(f.read(&tmp[0], 6));
61 -
        TEST(tmp == "test\r\n");
61 +
        TEST_EQ(tmp, "test\r\n");
62 62
    }
63 63
}
64 64
@@ -83,9 +83,9 @@
Loading
83 83
        TEST(f);
84 84
        std::string tmp;
85 85
        TEST(f >> tmp);
86 -
        TEST(tmp == "test");
86 +
        TEST_EQ(tmp, "test");
87 87
    }
88 -
    TEST(read_file(filename) == "test");
88 +
    TEST_EQ(read_file(filename), "test");
89 89
90 90
    // At end
91 91
    {
@@ -98,9 +98,9 @@
Loading
98 98
        f.clear();
99 99
        f.seekg(0, std::ios::beg);
100 100
        TEST(f >> tmp);
101 -
        TEST(tmp == "test");
101 +
        TEST_EQ(tmp, "test");
102 102
    }
103 -
    TEST(read_file(filename) == "test");
103 +
    TEST_EQ(read_file(filename), "test");
104 104
105 105
    create_file(filename, "test\r\n", data_type::binary);
106 106
    // Binary mode
@@ -110,7 +110,7 @@
Loading
110 110
        TEST(f);
111 111
        std::string tmp(6, '\0');
112 112
        TEST(f.read(&tmp[0], 6));
113 -
        TEST(tmp == "test\r\n");
113 +
        TEST_EQ(tmp, "test\r\n");
114 114
    }
115 115
}
116 116
@@ -120,7 +120,7 @@
Loading
120 120
    create_file(filename, "Hello\nWorld");
121 121
    create_file(filename2, "Foo\nBar");
122 122
    remove_file_at_exit _(filename);
123 -
    remove_file_at_exit _2(filename);
123 +
    remove_file_at_exit _2(filename2);
124 124
125 125
    // Move construct
126 126
    {
@@ -139,13 +139,13 @@
Loading
139 139
#endif
140 140
        TEST(f_old);
141 141
        TEST(f_old >> s);
142 -
        TEST(s == "Foo");
142 +
        TEST_EQ(s, "Foo");
143 143
        TEST(f_old >> s && s == "Bar");
144 144
145 145
        // new starts where the old was left of
146 146
        TEST(f_new);
147 147
        TEST(f_new >> s);
148 -
        TEST(s == "World");
148 +
        TEST_EQ(s, "World");
149 149
    }
150 150
    // Move assign
151 151
    {
@@ -167,13 +167,13 @@
Loading
167 167
#endif
168 168
            TEST(f_old);
169 169
            TEST(f_old >> s);
170 -
            TEST(s == "Foo");
170 +
            TEST_EQ(s, "Foo");
171 171
            TEST(f_old >> s && s == "Bar");
172 172
        }
173 173
        // new starts where the old was left of
174 174
        TEST(f_new);
175 175
        TEST(f_new >> s);
176 -
        TEST(s == "World");
176 +
        TEST_EQ(s, "World");
177 177
    }
178 178
    // Swap
179 179
    {
@@ -187,10 +187,10 @@
Loading
187 187
        // After swapping both are valid and where they left
188 188
        f_new.swap(f_old);
189 189
        TEST(f_old >> s);
190 -
        TEST(s == "Bar");
190 +
        TEST_EQ(s, "Bar");
191 191
192 192
        TEST(f_new >> s);
193 -
        TEST(s == "World");
193 +
        TEST_EQ(s, "World");
194 194
195 195
        f_new.close();
196 196
        swap(f_new, f_old);

@@ -49,41 +49,41 @@
Loading
49 49
        TEST(f.put('g'));
50 50
        // Read first char
51 51
        TEST(f.seekg(0));
52 -
        TEST(f.get() == 'a');
53 -
        TEST(f.gcount() == 1u);
52 +
        TEST_EQ(f.get(), 'a');
53 +
        TEST_EQ(f.gcount(), std::streamsize(1));
54 54
        // Skip next char
55 55
        TEST(f.seekg(1, std::ios::cur));
56 -
        TEST(f.get() == 'c');
57 -
        TEST(f.gcount() == 1u);
56 +
        TEST_EQ(f.get(), 'c');
57 +
        TEST_EQ(f.gcount(), std::streamsize(1));
58 58
        // Go back 1 char
59 59
        TEST(f.seekg(-1, std::ios::cur));
60 -
        TEST(f.get() == 'c');
61 -
        TEST(f.gcount() == 1u);
60 +
        TEST_EQ(f.get(), 'c');
61 +
        TEST_EQ(f.gcount(), std::streamsize(1));
62 62
63 63
        // Test switching between read->write->read
64 64
        // case 1) overwrite, flush, read
65 65
        TEST(f.seekg(1));
66 66
        TEST(f.put('B'));
67 67
        TEST(f.flush()); // Flush when changing out->in
68 -
        TEST(f.get() == 'c');
69 -
        TEST(f.gcount() == 1u);
68 +
        TEST_EQ(f.get(), 'c');
69 +
        TEST_EQ(f.gcount(), std::streamsize(1));
70 70
        TEST(f.seekg(1));
71 -
        TEST(f.get() == 'B');
72 -
        TEST(f.gcount() == 1u);
71 +
        TEST_EQ(f.get(), 'B');
72 +
        TEST_EQ(f.gcount(), std::streamsize(1));
73 73
        // case 2) overwrite, seek, read
74 74
        TEST(f.seekg(2));
75 75
        TEST(f.put('C'));
76 76
        TEST(f.seekg(3)); // Seek when changing out->in
77 -
        TEST(f.get() == 'd');
78 -
        TEST(f.gcount() == 1u);
77 +
        TEST_EQ(f.get(), 'd');
78 +
        TEST_EQ(f.gcount(), std::streamsize(1));
79 79
80 80
        // Check that sequence from start equals expected
81 81
        TEST(f.seekg(0));
82 -
        TEST(f.get() == 'a');
83 -
        TEST(f.get() == 'B');
84 -
        TEST(f.get() == 'C');
85 -
        TEST(f.get() == 'd');
86 -
        TEST(f.get() == 'e');
82 +
        TEST_EQ(f.get(), 'a');
83 +
        TEST_EQ(f.get(), 'B');
84 +
        TEST_EQ(f.get(), 'C');
85 +
        TEST_EQ(f.get(), 'd');
86 +
        TEST_EQ(f.get(), 'e');
87 87
88 88
        // Putback after flush is implementation defined
89 89
        // Boost.Nowide: Works
@@ -91,18 +91,24 @@
Loading
91 91
        TEST(f << std::flush);
92 92
        TEST(f.putback('e'));
93 93
        TEST(f.putback('d'));
94 -
        TEST(f.get() == 'd');
95 -
        TEST(f.get() == 'e');
94 +
        TEST_EQ(f.get(), 'd');
95 +
        TEST_EQ(f.get(), 'e');
96 +
        TEST(f << std::flush);
97 +
        TEST(f.unget());
98 +
        TEST_EQ(f.get(), 'e');
96 99
#endif
100 +
        // Put back different char
101 +
        TEST(f.putback('x'));
102 +
        TEST_EQ(f.get(), 'x');
97 103
        // Rest of sequence
98 -
        TEST(f.get() == 'f');
99 -
        TEST(f.get() == 'g');
100 -
        TEST(f.get() == EOF);
104 +
        TEST_EQ(f.get(), 'f');
105 +
        TEST_EQ(f.get(), 'g');
106 +
        TEST_EQ(f.get(), EOF);
101 107
102 108
        // Put back until front of file is reached
103 109
        f.clear();
104 110
        TEST(f.seekg(1));
105 -
        TEST(f.get() == 'B');
111 +
        TEST_EQ(f.get(), 'B');
106 112
        TEST(f.putback('B'));
107 113
        // Putting back multiple chars is not possible on all implementations after a seek/flush
108 114
#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
@@ -110,13 +116,34 @@
Loading
110 116
        TEST(!f.putback('x')); // At beginning of file -> No putback possible
111 117
        // Get characters that were putback to avoid MSVC bug https://github.com/microsoft/STL/issues/342
112 118
        f.clear();
113 -
        TEST(f.get() == 'a');
119 +
        TEST_EQ(f.get(), 'a');
114 120
#endif
115 -
        TEST(f.get() == 'B');
121 +
        TEST_EQ(f.get(), 'B');
116 122
        f.close();
117 123
    }
118 124
}
119 125
126 +
void test_switch_to_custom_buffer(const std::string& filename)
127 +
{
128 +
    // Switching the buffer after file stream was used is not always defined. So only test custom stream
129 +
#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
130 +
    nw::test::create_file(filename, "HelloWorld");
131 +
    nw::ifstream f(filename, std::ios::binary);
132 +
    std::string s(5, '\0');
133 +
    TEST(f.read(&s.front(), s.size()));
134 +
    TEST_EQ(s, "Hello");
135 +
    // Switch buffer
136 +
    std::string buffer(10, '\0');
137 +
    TEST_EQ(f.sync(), 0);
138 +
    TEST(f.rdbuf()->pubsetbuf(&buffer.front(), buffer.size()) == f.rdbuf());
139 +
    TEST(f >> s);
140 +
    TEST_EQ(s, "World");
141 +
    TEST_EQ(s, buffer.c_str()); // same should be in buffer and some trailing NULL bytes
142 +
#else
143 +
    (void)filename; // Suppress unused warning
144 +
#endif
145 +
}
146 +
120 147
// Reproducer for https://github.com/boostorg/nowide/issues/126
121 148
void test_getline_and_tellg(const char* filename)
122 149
{
@@ -133,13 +160,13 @@
Loading
133 160
    TEST(f);
134 161
    std::string line1, line2, line3;
135 162
    TEST(getline(f, line1));
136 -
    TEST(line1 == "Line 1");
163 +
    TEST_EQ(line1, "Line 1");
137 164
    const auto tg = f.tellg(); // This may cause issues
138 165
    TEST(tg > 0u);
139 166
    TEST(getline(f, line2));
140 -
    TEST(line2 == "Line 2");
167 +
    TEST_EQ(line2, "Line 2");
141 168
    TEST(getline(f, line3));
142 -
    TEST(line3 == "Line 3");
169 +
    TEST_EQ(line3, "Line 3");
143 170
}
144 171
145 172
// Test that a sync after a peek does not swallow newlines
@@ -160,7 +187,7 @@
Loading
160 187
        if(curChar == std::char_traits<char>::eof())
161 188
            break;
162 189
        f.sync();
163 -
        TEST(f.get() == char(curChar));
190 +
        TEST_EQ(f.get(), char(curChar));
164 191
    }
165 192
}
166 193
@@ -193,27 +220,55 @@
Loading
193 220
        else if(ctr % 15 == 0)
194 221
            TEST(f2.seekg(f2.tellg()));
195 222
        f1.swap(f2);
196 -
        TEST(f1.peek() == curChar2);
197 -
        TEST(f2.peek() == curChar1);
223 +
        TEST_EQ(f1.peek(), curChar2);
224 +
        TEST_EQ(f2.peek(), curChar1);
198 225
        if(ctr % 10 == 4)
199 226
            TEST(f1.seekg(f1.tellg()));
200 227
        else if(ctr % 15 == 4)
201 228
            TEST(f2.seekg(f2.tellg()));
202 -
        TEST(f1.get() == char(curChar2));
229 +
        TEST_EQ(f1.get(), char(curChar2));
203 230
        f1.swap(f2);
204 -
        TEST(f1.get() == char(curChar1));
231 +
        TEST_EQ(f1.get(), char(curChar1));
205 232
        ++ctr;
206 233
    }
207 234
}
208 235
236 +
void testPutback(const char* filename)
237 +
{
238 +
    nw::test::create_file(filename, "abc");
239 +
    // Does work for ifstreams
240 +
    {
241 +
        nw::ifstream f(filename);
242 +
        const int c = f.get();
243 +
        TEST(f.putback(static_cast<char>(c)));
244 +
        TEST_EQ(f.get(), c);
245 +
    }
246 +
    // Does work for io fstreams
247 +
    {
248 +
        nw::fstream f(filename);
249 +
        const int c = f.get();
250 +
        TEST(f.putback(static_cast<char>(c)));
251 +
        TEST_EQ(f.get(), c);
252 +
    }
253 +
    // Doesn't work for output fstreams
254 +
    {
255 +
        nw::fstream f(filename, std::ios::out);
256 +
        TEST(!f.putback('x'));
257 +
    }
258 +
}
259 +
209 260
// coverity [root_function]
210 261
void test_main(int, char** argv, char**)
211 262
{
212 263
    const std::string exampleFilename = std::string(argv[0]) + "-\xd7\xa9-\xd0\xbc-\xce\xbd.txt";
213 264
    const std::string exampleFilename2 = std::string(argv[0]) + "-\xd7\xa9-\xd0\xbc-\xce\xbd 2.txt";
214 265
266 +
    std::cout << "Putback" << std::endl;
267 +
    testPutback(exampleFilename.c_str());
268 +
215 269
    std::cout << "Complex IO" << std::endl;
216 270
    test_with_different_buffer_sizes(exampleFilename.c_str());
271 +
    test_switch_to_custom_buffer(exampleFilename.c_str());
217 272
218 273
    std::cout << "Regression tests" << std::endl;
219 274
    test_getline_and_tellg(exampleFilename.c_str());

@@ -39,7 +39,7 @@
Loading
39 39
    TEST(f);
40 40
    const char testData[] = "Hello World";
41 41
    constexpr size_t testDataSize = sizeof(testData);
42 -
    TEST(std::fwrite(testData, sizeof(char), testDataSize, f) == testDataSize);
42 +
    TEST_EQ(std::fwrite(testData, sizeof(char), testDataSize, f), testDataSize);
43 43
    std::fclose(f);
44 44
    {
45 45
#ifdef BOOST_WINDOWS
@@ -47,11 +47,11 @@
Loading
47 47
#else
48 48
        struct stat stdStat;
49 49
#endif /*  */
50 -
        TEST(boost::nowide::stat(filename.c_str(), &stdStat) == 0);
51 -
        TEST(stdStat.st_size == testDataSize);
50 +
        TEST_EQ(boost::nowide::stat(filename.c_str(), &stdStat), 0);
51 +
        TEST_EQ(static_cast<size_t>(stdStat.st_size), testDataSize);
52 52
        boost::nowide::stat_t boostStat;
53 -
        TEST(boost::nowide::stat(filename.c_str(), &boostStat) == 0);
54 -
        TEST(boostStat.st_size == testDataSize);
53 +
        TEST_EQ(boost::nowide::stat(filename.c_str(), &boostStat), 0);
54 +
        TEST_EQ(static_cast<size_t>(boostStat.st_size), testDataSize);
55 55
    }
56 56
57 57
#ifdef BOOST_WINDOWS
@@ -60,8 +60,8 @@
Loading
60 60
        struct _stat stdStat;
61 61
        // Simulate passing a struct that is 4 bytes smaller, e.g. if it uses 32 bit time field instead of 64 bit
62 62
        // Need to use the detail function directly
63 -
        TEST(boost::nowide::detail::stat(filename.c_str(), &stdStat, sizeof(stdStat) - 4u) == EINVAL);
64 -
        TEST(errno == EINVAL);
63 +
        TEST_EQ(boost::nowide::detail::stat(filename.c_str(), &stdStat, sizeof(stdStat) - 4u), EINVAL);
64 +
        TEST_EQ(errno, EINVAL);
65 65
    }
66 66
#endif
67 67

@@ -60,7 +60,7 @@
Loading
60 60
    std::minstd_rand rng(std::random_device{}());
61 61
    using pos_type = nw::filebuf::pos_type;
62 62
    const auto eofPos = pos_type(data.size());
63 -
    std::uniform_int_distribution<size_t> distr(0, static_cast<size_t>(eofPos));
63 +
    std::uniform_int_distribution<size_t> distr(0, static_cast<size_t>(eofPos) - 1);
64 64
    using traits = nw::filebuf::traits_type;
65 65
66 66
    const auto getData = [&](pos_type pos) { return traits::to_int_type(data[static_cast<size_t>(pos)]); };
@@ -68,17 +68,14 @@
Loading
68 68
    for(int i = 0; i < 100; i++)
69 69
    {
70 70
        const pos_type pos = distr(rng);
71 -
        TEST(buf.pubseekpos(pos) == pos);
72 -
        if(pos == eofPos)
73 -
            TEST(buf.sgetc() == traits::eof());
74 -
        else
75 -
            TEST(buf.sgetc() == getData(pos));
71 +
        TEST_EQ(buf.pubseekpos(pos), pos);
72 +
        TEST_EQ(buf.sgetc(), getData(pos));
76 73
    }
77 74
    // Seek to first and last as corner case tests
78 -
    TEST(buf.pubseekpos(0) == pos_type(0));
79 -
    TEST(buf.sgetc() == traits::to_int_type(data[0]));
80 -
    TEST(buf.pubseekpos(eofPos) == eofPos);
81 -
    TEST(buf.sgetc() == traits::eof());
75 +
    TEST_EQ(buf.pubseekpos(0), pos_type(0));
76 +
    TEST_EQ(buf.sgetc(), traits::to_int_type(data[0]));
77 +
    TEST_EQ(buf.pubseekpos(eofPos), eofPos);
78 +
    TEST_EQ(buf.sgetc(), traits::eof());
82 79
}
83 80
84 81
void test_pubseekoff(const std::string& filepath)
@@ -93,7 +90,7 @@
Loading
93 90
    using pos_type = nw::filebuf::pos_type;
94 91
    using off_type = nw::filebuf::off_type;
95 92
    const auto eofPos = pos_type(data.size());
96 -
    std::uniform_int_distribution<size_t> distr(0, static_cast<size_t>(eofPos));
93 +
    std::uniform_int_distribution<size_t> distr(0, static_cast<size_t>(eofPos) - 1);
97 94
    using traits = nw::filebuf::traits_type;
98 95
99 96
    const auto getData = [&](pos_type pos) { return traits::to_int_type(data[static_cast<size_t>(pos)]); };
@@ -104,38 +101,29 @@
Loading
104 101
    {
105 102
        // beg
106 103
        pos_type pos = distr(rng);
107 -
        TEST(buf.pubseekoff(pos, std::ios_base::beg) == pos);
108 -
        TEST(tellg() == pos);
109 -
        if(pos == eofPos)
110 -
            TEST(buf.sgetc() == traits::eof());
111 -
        else
112 -
            TEST(buf.sgetc() == getData(pos));
104 +
        TEST_EQ(buf.pubseekoff(pos, std::ios_base::beg), pos);
105 +
        TEST_EQ(tellg(), pos);
106 +
        TEST_EQ(buf.sgetc(), getData(pos));
113 107
        // cur
114 108
        off_type diff = static_cast<pos_type>(distr(rng)) - pos;
115 109
        pos += diff;
116 -
        TEST(buf.pubseekoff(diff, std::ios_base::cur) == pos);
117 -
        TEST(tellg() == pos);
118 -
        if(pos == eofPos)
119 -
            TEST(buf.sgetc() == traits::eof());
120 -
        else
121 -
            TEST(buf.sgetc() == getData(pos));
110 +
        TEST_EQ(buf.pubseekoff(diff, std::ios_base::cur), pos);
111 +
        TEST_EQ(tellg(), pos);
112 +
        TEST_EQ(buf.sgetc(), getData(pos));
122 113
        // end
123 114
        diff = static_cast<pos_type>(distr(rng)) - eofPos;
124 115
        pos = eofPos + diff;
125 -
        TEST(buf.pubseekoff(diff, std::ios_base::end) == pos);
126 -
        TEST(tellg() == pos);
127 -
        if(pos == eofPos)
128 -
            TEST(buf.sgetc() == traits::eof());
129 -
        else
130 -
            TEST(buf.sgetc() == getData(pos));
116 +
        TEST_EQ(buf.pubseekoff(diff, std::ios_base::end), pos);
117 +
        TEST_EQ(tellg(), pos);
118 +
        TEST_EQ(buf.sgetc(), getData(pos));
131 119
    }
132 120
    // Seek to first and last as corner case tests
133 -
    TEST(buf.pubseekoff(0, std::ios_base::beg) == pos_type(0));
134 -
    TEST(tellg() == pos_type(0));
135 -
    TEST(buf.sgetc() == traits::to_int_type(data[0]));
136 -
    TEST(buf.pubseekoff(0, std::ios_base::end) == eofPos);
137 -
    TEST(tellg() == eofPos);
138 -
    TEST(buf.sgetc() == traits::eof());
121 +
    TEST_EQ(buf.pubseekoff(0, std::ios_base::beg), pos_type(0));
122 +
    TEST_EQ(tellg(), pos_type(0));
123 +
    TEST_EQ(buf.sgetc(), traits::to_int_type(data[0]));
124 +
    TEST_EQ(buf.pubseekoff(0, std::ios_base::end), eofPos);
125 +
    TEST_EQ(tellg(), eofPos);
126 +
    TEST_EQ(buf.sgetc(), traits::eof());
139 127
}
140 128
141 129
void test_64_bit_seek(const std::string& filepath)
@@ -149,10 +137,10 @@
Loading
149 137
#pragma warning(push)
150 138
#pragma warning(disable : 4127)
151 139
#endif
152 -
    // if we can't use 64 bit offsets throught the API, don't test anything
140 +
    // if we can't use 64 bit offsets through the API, don't test anything
153 141
    // coverity[result_independent_of_operands]
154 142
    if(offset == nw::filebuf::off_type(0))
155 -
        return; // coverity[dead_error_line]
143 +
        return; // LCOV_EXCL_LINE coverity[dead_error_line]
156 144
#ifdef BOOST_MSVC
157 145
#pragma warning(pop)
158 146
#endif
@@ -163,11 +151,11 @@
Loading
163 151
    nw::filebuf buf;
164 152
    TEST(buf.open(filepath, std::ios_base::in | std::ios_base::binary) == &buf);
165 153
    const std::streampos knownPos = 2;
166 -
    TEST(buf.pubseekpos(knownPos) == knownPos); // Just to make sure we know where we are
154 +
    TEST_EQ(buf.pubseekpos(knownPos), knownPos); // Just to make sure we know where we are
167 155
    const std::streampos newPos = buf.pubseekoff(offset, std::ios_base::cur);
168 156
    // On 32 bit mode or when seek beyond EOF is not allowed, the current position should be unchanged
169 157
    if(newPos == std::streampos(-1))
170 -
        TEST(buf.pubseekoff(0, std::ios_base::cur) == knownPos);
158 +
        TEST_EQ(buf.pubseekoff(0, std::ios_base::cur), knownPos);
171 159
    else
172 160
    {
173 161
#if !BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
@@ -175,8 +163,8 @@
Loading
175 163
        if(newPos == knownPos)
176 164
            offset = 0; // LCOV_EXCL_LINE
177 165
#endif
178 -
        TEST(newPos == offset + knownPos);
179 -
        TEST(buf.pubseekoff(0, std::ios_base::cur) == newPos);
166 +
        TEST_EQ(newPos, offset + knownPos);
167 +
        TEST_EQ(buf.pubseekoff(0, std::ios_base::cur), newPos);
180 168
    }
181 169
}
182 170
@@ -221,8 +209,8 @@
Loading
221 209
        buf1.close();
222 210
        TEST(!buf1.is_open());
223 211
        TEST(!buf2.is_open());
224 -
        TEST(read_file(filepath) == "FooBar");
225 -
        TEST(read_file(filepath2) == "HelloWorld");
212 +
        TEST_EQ(read_file(filepath), "FooBar");
213 +
        TEST_EQ(read_file(filepath2), "HelloWorld");
226 214
    }
227 215
    // Check: mode, owns_buffer
228 216
    {
@@ -231,24 +219,24 @@
Loading
231 219
        buf1.pubsetbuf(buffer, sizeof(buffer));
232 220
        TEST(buf1.open(filepath, std::ios_base::out) == &buf1);
233 221
        TEST(buf2.open(filepath2, std::ios_base::in) == &buf2);
234 -
        TEST(buf1.sputc('B') == 'B');
235 -
        TEST(buf2.sbumpc() == 'H');
222 +
        TEST_EQ(buf1.sputc('B'), 'B');
223 +
        TEST_EQ(buf2.sbumpc(), 'H');
236 224
        buf1.swap(buf2);
237 225
        // Trying to read in write mode or other way round should fail
238 -
        TEST(buf1.sputc('x') == eof);
239 -
        TEST(buf2.sbumpc() == eof);
240 -
        TEST(buf1.sbumpc() == 'e');
241 -
        TEST(buf2.sputc('a') == 'a');
226 +
        TEST_EQ(buf1.sputc('x'), eof);
227 +
        TEST_EQ(buf2.sbumpc(), eof);
228 +
        TEST_EQ(buf1.sbumpc(), 'e');
229 +
        TEST_EQ(buf2.sputc('a'), 'a');
242 230
        buf2.swap(buf1);
243 -
        TEST(buf2.sputc('x') == eof);
244 -
        TEST(buf1.sbumpc() == eof);
245 -
        TEST(buf2.sbumpc() == 'l');
246 -
        TEST(buf1.sputn("zXYZ", 4) == 4);
231 +
        TEST_EQ(buf2.sputc('x'), eof);
232 +
        TEST_EQ(buf1.sbumpc(), eof);
233 +
        TEST_EQ(buf2.sbumpc(), 'l');
234 +
        TEST_EQ(buf1.sputn("zXYZ", 4), 4);
247 235
        swap(buf2, buf1);
248 236
        buf1.close();
249 237
        buf2.close();
250 -
        TEST(read_file(filepath) == "BazXYZ");
251 -
        TEST(read_file(filepath2) == "HelloWorld");
238 +
        TEST_EQ(read_file(filepath), "BazXYZ");
239 +
        TEST_EQ(read_file(filepath2), "HelloWorld");
252 240
    }
253 241
    // Check: last_char, gptr, eback
254 242
    {
@@ -258,22 +246,22 @@
Loading
258 246
        TEST(buf1.open(filepath, std::ios_base::in) == &buf1);
259 247
        TEST(buf2.open(filepath2, std::ios_base::in) == &buf2);
260 248
        // Peek
261 -
        TEST(buf1.sgetc() == 'B');
262 -
        TEST(buf2.sgetc() == 'H');
249 +
        TEST_EQ(buf1.sgetc(), 'B');
250 +
        TEST_EQ(buf2.sgetc(), 'H');
263 251
        swap(buf1, buf2);
264 -
        TEST(buf2.sgetc() == 'B');
265 -
        TEST(buf1.sgetc() == 'H');
252 +
        TEST_EQ(buf2.sgetc(), 'B');
253 +
        TEST_EQ(buf1.sgetc(), 'H');
266 254
        // Advance
267 -
        TEST(buf2.sbumpc() == 'B');
268 -
        TEST(buf1.sbumpc() == 'H');
269 -
        TEST(buf2.sbumpc() == 'a');
270 -
        TEST(buf1.sbumpc() == 'e');
255 +
        TEST_EQ(buf2.sbumpc(), 'B');
256 +
        TEST_EQ(buf1.sbumpc(), 'H');
257 +
        TEST_EQ(buf2.sbumpc(), 'a');
258 +
        TEST_EQ(buf1.sbumpc(), 'e');
271 259
        swap(buf1, buf2);
272 -
        TEST(buf1.sbumpc() == 'z');
273 -
        TEST(buf2.sbumpc() == 'l');
260 +
        TEST_EQ(buf1.sbumpc(), 'z');
261 +
        TEST_EQ(buf2.sbumpc(), 'l');
274 262
        swap(buf1, buf2);
275 -
        TEST(buf2.sgetc() == 'X');
276 -
        TEST(buf1.sgetc() == 'l');
263 +
        TEST_EQ(buf2.sgetc(), 'X');
264 +
        TEST_EQ(buf1.sgetc(), 'l');
277 265
    }
278 266
    // Check: pptr, epptr
279 267
    {
@@ -282,31 +270,31 @@
Loading
282 270
        buf1.pubsetbuf(0, 0);
283 271
        TEST(buf1.open(filepath, std::ios_base::out) == &buf1);
284 272
        TEST(buf2.open(filepath2, std::ios_base::out) == &buf2);
285 -
        TEST(buf1.sputc('1') == '1');
286 -
        TEST(buf2.sputc('a') == 'a');
273 +
        TEST_EQ(buf1.sputc('1'), '1');
274 +
        TEST_EQ(buf2.sputc('a'), 'a');
287 275
        swap(buf1, buf2);
288 276
        // buf1: filepath2, buf2: filepath
289 -
        TEST(buf1.sputc('b') == 'b');
290 -
        TEST(buf2.sputc('2') == '2');
277 +
        TEST_EQ(buf1.sputc('b'), 'b');
278 +
        TEST_EQ(buf2.sputc('2'), '2');
291 279
        // Sync and check if file was written
292 -
        TEST(buf1.pubsync() == 0);
293 -
        TEST(read_file(filepath2) == "ab");
294 -
        TEST(buf2.pubsync() == 0);
295 -
        TEST(read_file(filepath) == "12");
280 +
        TEST_EQ(buf1.pubsync(), 0);
281 +
        TEST_EQ(read_file(filepath2), "ab");
282 +
        TEST_EQ(buf2.pubsync(), 0);
283 +
        TEST_EQ(read_file(filepath), "12");
296 284
        swap(buf1, buf2);
297 285
        // buf1: filepath, buf2: filepath2
298 -
        TEST(buf1.pubsync() == 0);
299 -
        TEST(read_file(filepath) == "12");
300 -
        TEST(buf2.pubsync() == 0);
301 -
        TEST(read_file(filepath2) == "ab");
302 -
        TEST(buf1.sputc('3') == '3');
303 -
        TEST(buf2.sputc('c') == 'c');
286 +
        TEST_EQ(buf1.pubsync(), 0);
287 +
        TEST_EQ(read_file(filepath), "12");
288 +
        TEST_EQ(buf2.pubsync(), 0);
289 +
        TEST_EQ(read_file(filepath2), "ab");
290 +
        TEST_EQ(buf1.sputc('3'), '3');
291 +
        TEST_EQ(buf2.sputc('c'), 'c');
304 292
        swap(buf1, buf2);
305 293
        // buf1: filepath2, buf2: filepath
306 -
        TEST(buf1.pubsync() == 0);
307 -
        TEST(read_file(filepath2) == "abc");
308 -
        TEST(buf2.pubsync() == 0);
309 -
        TEST(read_file(filepath) == "123");
294 +
        TEST_EQ(buf1.pubsync(), 0);
295 +
        TEST_EQ(read_file(filepath2), "abc");
296 +
        TEST_EQ(buf2.pubsync(), 0);
297 +
        TEST_EQ(read_file(filepath), "123");
310 298
    }
311 299
}
312 300

@@ -11,6 +11,7 @@
Loading
11 11
12 12
#include <boost/nowide/replacement.hpp>
13 13
#include <boost/nowide/utf/utf.hpp>
14 +
#include <cassert>
14 15
#include <cstdint>
15 16
#include <locale>
16 17
@@ -48,6 +49,11 @@
Loading
48 49
    template<typename CharType, int CharSize = sizeof(CharType)>
49 50
    class utf8_codecvt;
50 51
52 +
#ifdef BOOST_MSVC
53 +
#pragma warning(push)
54 +
#pragma warning(disable : 4996) // Disable deprecation warning for std::codecvt<char16_t, char, ...>
55 +
#endif
56 +
51 57
    /// Specialization for the UTF-8 <-> UTF-16 variant of the std::codecvt implementation
52 58
    template<typename CharType>
53 59
    class BOOST_SYMBOL_VISIBLE utf8_codecvt<CharType, 2> : public std::codecvt<CharType, char, std::mbstate_t>
@@ -58,6 +64,10 @@
Loading
58 64
        utf8_codecvt(size_t refs = 0) : std::codecvt<CharType, char, std::mbstate_t>(refs)
59 65
        {}
60 66
67 +
#ifdef BOOST_MSVC
68 +
#pragma warning(pop)
69 +
#endif
70 +
61 71
    protected:
62 72
        using uchar = CharType;
63 73
@@ -81,8 +91,10 @@
Loading
81 91
            return false;
82 92
        }
83 93
94 +
        // LCOV_EXCL_START
84 95
        int do_length(std::mbstate_t& std_state, const char* from, const char* from_end, size_t max) const override
85 96
        {
97 +
            // LCOV_EXCL_STOP
86 98
            using utf16_traits = utf::utf_traits<uchar, 2>;
87 99
            std::uint16_t state = detail::read_state(std_state);
88 100
            const char* save_from = from;
@@ -225,11 +237,7 @@
Loading
225 237
                        ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER;
226 238
                    }
227 239
                }
228 -
                if(!utf::is_valid_codepoint(ch))
229 -
                {
230 -
                    r = std::codecvt_base::error;
231 -
                    break;
232 -
                }
240 +
                assert(utf::is_valid_codepoint(ch)); // Any valid UTF16 sequence is a valid codepoint
233 241
                int len = utf::utf_traits<char>::width(ch);
234 242
                if(to_end - to < len)
235 243
                {
@@ -248,6 +256,11 @@
Loading
248 256
        }
249 257
    };
250 258
259 +
#ifdef BOOST_MSVC
260 +
#pragma warning(push)
261 +
#pragma warning(disable : 4996) // Disable deprecation warning for std::codecvt<char32_t, char, ...>
262 +
#endif
263 +
251 264
    /// Specialization for the UTF-8 <-> UTF-32 variant of the std::codecvt implementation
252 265
    template<typename CharType>
253 266
    class BOOST_SYMBOL_VISIBLE utf8_codecvt<CharType, 4> : public std::codecvt<CharType, char, std::mbstate_t>
@@ -256,6 +269,10 @@
Loading
256 269
        utf8_codecvt(size_t refs = 0) : std::codecvt<CharType, char, std::mbstate_t>(refs)
257 270
        {}
258 271
272 +
#ifdef BOOST_MSVC
273 +
#pragma warning(pop)
274 +
#endif
275 +
259 276
    protected:
260 277
        using uchar = CharType;
261 278
@@ -296,7 +313,7 @@
Loading
296 313
                }
297 314
                max--;
298 315
            }
299 -
            return from - start_from;
316 +
            return static_cast<int>(from - start_from);
300 317
        }
301 318
302 319
        std::codecvt_base::result do_in(std::mbstate_t& /*state*/,
Files Coverage
include/boost/nowide 89.34%
src 96.23%
test 98.65%
Project Totals (30 files) 95.93%
1
codecov:
2
  max_report_age: off
3
  require_ci_to_pass: yes
4
  notify:
5
    after_n_builds: 2
6
    wait_for_ci: yes
7
comment:
8
  layout: "diff, files"
9
ignore:
10
  - "test/test.hpp"
11
  - "test/file_test_helpers.cpp"
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading