boostorg / nowide
Showing 2 of 3 files from the diff.

@@ -271,9 +271,12 @@
Loading
271 271
            bool result;
272 272
            if(pptr())
273 273
            {
274 +
                // Only flush if anything was written, otherwise behavior of fflush is undefined. I.e.:
275 +
                // - Buffered mode: pptr was set to buffer_ and advanced
276 +
                // - Unbuffered mode: pptr set to last_char_
277 +
                const bool has_prev_write = pptr() != buffer_;
274 278
                result = overflow() != EOF;
275 -
                // Only flush if anything was written, otherwise behavior of fflush is undefined
276 -
                if(std::fflush(file_) != 0)
279 +
                if(has_prev_write && std::fflush(file_) != 0)
277 280
                    result = false;
278 281
            } else
279 282
                result = stop_reading();

@@ -9,10 +9,12 @@
Loading
9 9
10 10
#include "file_test_helpers.hpp"
11 11
#include "test.hpp"
12 +
#include <algorithm>
12 13
#include <cstdint>
13 14
#include <random>
14 15
#include <string>
15 16
#include <type_traits>
17 +
#include <vector>
16 18
17 19
namespace nw = boost::nowide;
18 20
using namespace boost::nowide::test;
@@ -171,6 +173,200 @@
Loading
171 173
    }
172 174
}
173 175
176 +
void test_read_write_switch(const std::string& filepath, bool binary)
177 +
{
178 +
    // Switching between read and write requires a seek or (for W->R) a sync
179 +
    remove_file_at_exit _(filepath);
180 +
    const std::string data = "1234567890";
181 +
    auto flags = std::ios_base::in | std::ios_base::out | std::ios_base::trunc;
182 +
    if(binary)
183 +
        flags |= std::ios_base::binary;
184 +
    nw::filebuf buf;
185 +
    TEST(buf.open(filepath, flags));
186 +
    TEST_EQ(buf.sputn(data.data(), data.size()), static_cast<std::streamsize>(data.size()));
187 +
    // W->R via seek
188 +
    buf.pubseekpos(0);
189 +
    TEST_EQ(buf.sbumpc(), '1');
190 +
    // R->W via seek
191 +
    const auto pos = buf.pubseekoff(0, std::ios_base::cur);
192 +
    TEST(pos != std::streampos(-1));
193 +
    buf.sputc('b');
194 +
    // W->R via sync
195 +
    TEST(buf.pubsync() == 0);
196 +
    TEST_EQ(buf.sbumpc(), '3');
197 +
    // R->W via seek
198 +
    const auto pos2 = buf.pubseekoff(0, std::ios_base::cur);
199 +
    buf.sputc('c');
200 +
    // Read right back
201 +
    TEST_EQ(buf.pubseekpos(pos2), pos2);
202 +
    TEST_EQ(buf.sbumpc(), 'c');
203 +
    // R->W
204 +
    buf.pubseekoff(0, std::ios_base::cur);
205 +
    buf.sputc('d');
206 +
    // Sync & seek
207 +
    TEST(buf.pubsync() == 0);
208 +
    TEST(buf.pubseekoff(0, std::ios_base::cur) != std::streampos(-1));
209 +
    TEST_EQ(buf.sbumpc(), '6');
210 +
    // R->W
211 +
    buf.pubseekoff(0, std::ios_base::cur);
212 +
    buf.sputc('e');
213 +
    // Seek & sync
214 +
    TEST(buf.pubseekoff(0, std::ios_base::cur) != std::streampos(-1));
215 +
    TEST(buf.pubsync() == 0);
216 +
    TEST_EQ(buf.sbumpc(), '8');
217 +
218 +
    buf.close();
219 +
    TEST_EQ(read_file(filepath), "1b3cd6e890");
220 +
}
221 +
222 +
void subtest_sync(const std::string& filepath, bool binary, const std::string& data)
223 +
{
224 +
    nw::filebuf buf;
225 +
    // Use a small buffer to force filling it up w/o requiring to write lot's of data
226 +
    char buffer[3];
227 +
    buf.pubsetbuf(buffer, sizeof(buffer));
228 +
    auto flags = std::ios_base::out | std::ios_base::trunc;
229 +
    if(binary)
230 +
        flags |= std::ios_base::binary;
231 +
232 +
    // Do a series of single-char and multi-char writes with varying size combinations
233 +
    // Especially test the case of only single-char and only multi-char ops
234 +
    for(unsigned singleCharOps = 0; singleCharOps <= 3; ++singleCharOps)
235 +
    {
236 +
        // Write less than buffer size, 1 or 2 buffers or even more, assuming buffer size of 3
237 +
        for(size_t bufSize : {0, 2, 3, 6, 7})
238 +
        {
239 +
            if(singleCharOps + bufSize == 0u)
240 +
                continue;
241 +
            TEST(buf.open(filepath, flags));
242 +
            for(size_t i = 0; i < data.size();)
243 +
            {
244 +
                TEST_CONTEXT("sc:" << singleCharOps << " buf:" << bufSize << " i:" << i);
245 +
                for(unsigned j = 0; j < singleCharOps && i < data.size(); ++j, ++i)
246 +
                {
247 +
                    using CharTraits = nw::filebuf::traits_type;
248 +
                    TEST_EQ(buf.sputc(data[i]), CharTraits::to_int_type(data[i]));
249 +
                }
250 +
                if(bufSize != 0u)
251 +
                {
252 +
                    const auto remainSize = static_cast<std::streamsize>(std::min(data.size() - i, bufSize));
253 +
                    TEST_EQ(buf.sputn(&data[i], remainSize), remainSize);
254 +
                    i += static_cast<size_t>(remainSize);
255 +
                }
256 +
                TEST_EQ(buf.pubsync(), 0);
257 +
                TEST_EQ(read_file(filepath, binary ? data_type::binary : data_type::text), data.substr(0, i));
258 +
            }
259 +
            TEST(buf.close());
260 +
            TEST_EQ(read_file(filepath, binary ? data_type::binary : data_type::text), data);
261 +
        }
262 +
    }
263 +
}
264 +
265 +
void subtest_singlechar_positioning(const std::string& filepath, bool binary, const std::string& data)
266 +
{
267 +
    nw::filebuf buf;
268 +
    // Use a small buffer to force filling it up w/o requiring to write lot's of data
269 +
    char buffer[3];
270 +
    buf.pubsetbuf(buffer, sizeof(buffer));
271 +
    auto flags = std::ios_base::in | std::ios_base::out | std::ios_base::trunc;
272 +
    if(binary)
273 +
        flags |= std::ios_base::binary;
274 +
    TEST(buf.open(filepath, flags));
275 +
276 +
    // Put each char and record its position
277 +
    std::vector<nw::filebuf::pos_type> pos(data.size());
278 +
    for(unsigned i = 0; i < data.size(); ++i)
279 +
    {
280 +
        buf.sputc(data[i]);
281 +
        pos[i] = buf.pubseekoff(0, std::ios_base::cur);
282 +
    }
283 +
    // Go back to start and verify reading yields the same data and positions
284 +
    buf.pubseekoff(0, std::ios_base::beg);
285 +
    for(unsigned i = 0; i < data.size(); ++i)
286 +
    {
287 +
        TEST_CONTEXT("Position " << i);
288 +
        using CharTraits = nw::filebuf::traits_type;
289 +
        TEST_EQ(buf.sbumpc(), CharTraits::to_int_type(data[i]));
290 +
        TEST_EQ(buf.pubseekoff(0, std::ios_base::cur), pos[i]);
291 +
    }
292 +
}
293 +
294 +
void subtest_singlechar_multichar_reads(const std::string& filepath, bool binary, const std::string& data)
295 +
{
296 +
    create_file(filepath, data, binary ? data_type::binary : data_type::text);
297 +
    nw::filebuf buf;
298 +
    // Use a small buffer to force filling it up w/o requiring to write lot's of data
299 +
    char buffer[3];
300 +
    buf.pubsetbuf(buffer, sizeof(buffer));
301 +
    std::ios_base::openmode flags = std::ios_base::in;
302 +
    if(binary)
303 +
        flags |= std::ios_base::binary;
304 +
    TEST(buf.open(filepath, flags));
305 +
306 +
    // Do a series of single-char and multi-char reads with varying size combinations
307 +
    // Especially test the case of only single-char and only multi-char ops
308 +
    for(unsigned singleCharOps = 0; singleCharOps <= 3; ++singleCharOps)
309 +
    {
310 +
        // Read less than buffer size, 1 or 2 buffers or even more, assuming buffer size of 3
311 +
        for(size_t bufSize : {0, 2, 3, 6, 7})
312 +
        {
313 +
            if(singleCharOps + bufSize == 0u)
314 +
                continue;
315 +
316 +
            std::string outBuf(bufSize, '\0');
317 +
            buf.pubseekoff(0, std::ios_base::beg);
318 +
            for(size_t i = 0; i < data.size();)
319 +
            {
320 +
                TEST_CONTEXT("sc:" << singleCharOps << " buf:" << bufSize << " i:" << i);
321 +
                for(unsigned j = 0; j < singleCharOps && i < data.size(); ++j, ++i)
322 +
                {
323 +
                    using CharTraits = nw::filebuf::traits_type;
324 +
                    TEST_EQ(buf.sbumpc(), CharTraits::to_int_type(data[i]));
325 +
                }
326 +
                if(bufSize == 0u)
327 +
                    continue;
328 +
                const size_t readSize = std::min(data.size() - i, bufSize);
329 +
                TEST_EQ(buf.sgetn(&outBuf.front(), bufSize), static_cast<std::streamsize>(readSize));
330 +
                if(readSize < bufSize)
331 +
                    outBuf.resize(readSize);
332 +
                TEST_EQ(outBuf, data.substr(i, readSize));
333 +
                i += bufSize;
334 +
            }
335 +
        }
336 +
    }
337 +
}
338 +
339 +
void test_textmode(const std::string& filepath)
340 +
{
341 +
    remove_file_at_exit _(filepath);
342 +
    // Test input, output and getting the file position works for text files with newlines
343 +
    const std::string data = []() {
344 +
        // Some simple test data
345 +
        std::string result = "1234567890";
346 +
        // Line break after every char
347 +
        result.reserve(result.size() + 27 * 2);
348 +
        for(char c = 'a'; c <= 'z'; ++c)
349 +
            (result += c) += '\n';
350 +
        // Some continuous line breaks
351 +
        result.append(4, '\n');
352 +
        return result;
353 +
    }();
354 +
    subtest_singlechar_positioning(filepath, false, data);
355 +
    subtest_singlechar_multichar_reads(filepath, false, data);
356 +
    subtest_sync(filepath, false, data);
357 +
}
358 +
359 +
// Almost the same test as test_textmode but uses a binary stream.
360 +
// Useful as the buffer handling is very different
361 +
void test_binarymode(const std::string& filepath)
362 +
{
363 +
    remove_file_at_exit _(filepath);
364 +
    const std::string data = "123" + create_random_data(65, data_type::binary);
365 +
    subtest_singlechar_positioning(filepath, true, data);
366 +
    subtest_singlechar_multichar_reads(filepath, true, data);
367 +
    subtest_sync(filepath, true, data);
368 +
}
369 +
174 370
void test_swap(const std::string& filepath)
175 371
{
176 372
    const std::string filepath2 = filepath + ".2";
@@ -305,11 +501,16 @@
Loading
305 501
void test_main(int, char** argv, char**)
306 502
{
307 503
    const std::string exampleFilename = std::string(argv[0]) + "-\xd7\xa9-\xd0\xbc-\xce\xbd.txt";
308 -
309 504
    test_open_close(exampleFilename);
310 505
    test_pubseekpos(exampleFilename);
311 506
    test_pubseekoff(exampleFilename);
312 507
    test_64_bit_seek(exampleFilename);
508 +
    std::cout << "Testing text mode\n";
509 +
    test_read_write_switch(exampleFilename, false);
510 +
    test_textmode(exampleFilename);
511 +
    std::cout << "Testing binary mode\n";
512 +
    test_read_write_switch(exampleFilename, true);
513 +
    test_binarymode(exampleFilename);
313 514
// These tests are only useful for the nowide filebuf and are known to fail for
314 515
// std::filebuf due to bugs in libc++
315 516
#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
Files Coverage
include/boost/nowide 98.07%
src 98.07%
test 100.00%
Project Totals (33 files) 99.37%
Appveyor
Build #wtnte6v9axka9n2s -
APPVEYOR_BUILD_WORKER_IMAGE=Visual Studio 2019
Github Actions
Build #2384333798 -

No yaml found.

Create your codecov.yml to customize your Codecov experience

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