Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/src/Progmasoft/Catch3/Generators/Text.hpp
blob: 05df8c005aef60df8ee9711df04932853277a103 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Copyright (c) 2026 Progmasoft.
// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1
#pragma once

#include <Progmasoft/Catch3/Detail/Sequence.hpp>
#include <Progmasoft/Catch3/Generator.hpp>
#include <cstddef>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

namespace Progmasoft::Catch3
{

    /// The 95 printable ASCII characters, space through tilde, with the
    /// lower-case letter `a` first so that it is the simplest character.
    [[nodiscard]] inline std::string
    PrintableAscii()
    {
        std::string alphabet = "a";
        for (char character = ' '; character <= '~'; ++character)
        {
            if (character != 'a')
            {
                alphabet.push_back(character);
            }
        }
        return alphabet;
    }

    /// Generates byte strings whose characters are drawn from @p alphabet
    /// and whose length lies in the inclusive range.
    ///
    /// The alphabet order is the simplicity order: shrinking first shortens
    /// the string and then replaces single characters with earlier alphabet
    /// characters, earliest first. Characters are bytes; the generator does
    /// not interpret an encoding, so supply an alphabet that is valid for
    /// the code under test.
    [[nodiscard]] inline Generator<std::string>
    Text(std::string alphabet = PrintableAscii(),
         std::size_t minimumLength = 0,
         std::size_t maximumLength = 32)
    {
        if (alphabet.empty())
        {
            Detail::RejectInvalidArgument(
                "Text requires a non-empty alphabet");
        }
        if (minimumLength > maximumLength)
        {
            Detail::RejectInvalidArgument(
                "Text requires minimumLength <= maximumLength");
        }
        if (maximumLength > Detail::kMaximumSequenceLength)
        {
            Detail::RejectInvalidArgument(
                "Text maximumLength exceeds the supported sequence length");
        }

        const auto shared
            = std::make_shared<const std::string>(std::move(alphabet));
        return MakeGenerator<std::string>(
            [shared, minimumLength, maximumLength](Random &random) {
                const std::size_t length
                    = random.Between(minimumLength, maximumLength);
                std::string text;
                text.reserve(length);
                for (std::size_t index = 0; index < length; ++index)
                {
                    text.push_back((*shared)[random.Between(
                        std::size_t{ 0 },
                        shared->size() - 1)]);
                }
                return text;
            },
            [shared, minimumLength](const std::string &value) {
                return Detail::ShrinkSequence(
                    value,
                    minimumLength,
                    [&shared](char character) {
                        const std::size_t position = shared->find(character);
                        if (position == std::string::npos)
                        {
                            return std::string{};
                        }
                        return shared->substr(0, position);
                    });
            });
    }

} // namespace Progmasoft::Catch3