CoolFace
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 2d agoView on Hugging Face
0likes1.1kdownloads
test-grammar-integration.cpp1496 linesDownload Raw Back to tests
1#ifdef NDEBUG2#undef NDEBUG3#endif4 5#include "json-schema-to-grammar.h"6 7#include "../src/unicode.h"8#include "../src/llama-grammar.h"9 10#include "json.h"11 12#include <cassert>13#include <string>14#include <vector>15 16using json = common_json;17 18static llama_grammar * build_grammar_with_root(const std::string & grammar_str, const char * grammar_root) {19    return llama_grammar_init_impl(nullptr, grammar_str.c_str(), grammar_root, false, nullptr, 0, nullptr, 0);20}21 22static llama_grammar * build_grammar(const std::string & grammar_str) {23    return build_grammar_with_root(grammar_str, "root");24}25 26static bool test_build_grammar_fails(const std::string & grammar_str) {27    fprintf(stderr, "⚫ Testing failure for grammar: %s\n", grammar_str.c_str());28    bool grammar_fails = false;29    llama_grammar * grammar = build_grammar(grammar_str);30    if (grammar != nullptr) {31        fprintf(stderr, "  ❌ Expected build failure, but succeeded\n");32    } else {33        grammar_fails = true;34        fprintf(stdout, "  ✅︎\n");35    }36    return grammar_fails;37}38 39struct token_and_piece {40    llama_token token;41    std::string piece;42};43 44// token() encodes a 32-bit ID as 5 bytes: a 0xff marker followed by the ID in big-endian order.45static std::string token(llama_token id) {46    return std::string{47        static_cast<char>(0xff),48        static_cast<char>((id >> 24) & 0xff),49        static_cast<char>((id >> 16) & 0xff),50        static_cast<char>((id >> 8) & 0xff),51        static_cast<char>(id & 0xff)52    };53}54 55// parse_tokens() parses the token encodes above and UTF-8 text.56static std::vector<token_and_piece> parse_tokens(const std::string & input) {57    std::vector<token_and_piece> result;58    result.reserve(input.size());59    size_t offset = 0;60    while (offset < input.size()) {61        try {62            if (static_cast<unsigned char>(input[offset]) == 0xff) {63                if (offset + 5 > input.size()) {64                    throw std::runtime_error("not enough bytes for token id");65                }66                uint32_t val =67                    (static_cast<unsigned char>(input[offset + 1]) << 24) |68                    (static_cast<unsigned char>(input[offset + 2]) << 16) |69                    (static_cast<unsigned char>(input[offset + 3]) << 8)  |70                    (static_cast<unsigned char>(input[offset + 4]));71                auto piece = "<[" + std::to_string(val) + "]>";72                result.push_back({static_cast<llama_token>(val), piece});73                offset += 5;74            } else {75                uint32_t cpt = unicode_cpt_from_utf8(input, offset);76                result.push_back({0, unicode_cpt_to_utf8(cpt)});77            }78        } catch (const std::invalid_argument & /*ex*/) {79            // Silently ignore invalid UTF-8 input to avoid leaking the exception beyond llama_tokenize80            ++offset;81            result.push_back({0, unicode_cpt_to_utf8(0xFFFD)}); // replacement character82        }83    }84    return result;85}86 87static bool match_string(const std::string & input, llama_grammar * grammar) {88    const auto parsed = parse_tokens(input);89 90    auto & stacks_cur = llama_grammar_get_stacks(grammar);91 92    for (const auto & in : parsed) {93        try {94            llama_grammar_accept_token(*grammar, in.token, in.piece);95        } catch (const std::runtime_error & /*e*/) {96            // normally this shouldn't get hit because of llama_grammar_apply97            return false;98        }99 100        if (stacks_cur.empty()) {101            // no stacks means that the grammar failed to match at this point102            return false;103        }104    }105 106    for (const auto & stack : stacks_cur) {107        if (stack.empty()) {108            // An empty stack means that the grammar has been completed109            return true;110        }111    }112 113    return false;114}115 116static void test(const std::string & test_desc, const std::string & grammar_str, const std::vector<std::string> & passing_strings, const std::vector<std::string> & failing_strings) {117    fprintf(stderr, "⚫ Testing %s\n%s\n", test_desc.c_str(), grammar_str.c_str());118    fflush(stderr);119 120    auto * grammar = build_grammar(grammar_str);121 122    // Save the original grammar stacks so that we can reset after every new string we want to test123    const llama_grammar_stacks stacks_org = llama_grammar_get_stacks(grammar); // copy124 125    llama_grammar_stacks & stacks_cur = llama_grammar_get_stacks(grammar);126 127    fprintf(stderr, "  🔵 Valid strings:\n");128 129    // Passing strings130    for (const auto & test_string : passing_strings) {131        fprintf(stderr, "    \"%s\" ", test_string.c_str());132        fflush(stderr);133 134        bool matched = match_string(test_string, grammar);135 136        if (!matched) {137            fprintf(stderr, "❌ (failed to match)\n");138 139            // DEBUG: Write strings to files so that we can analyze more easily with gbnf-validator program to see exactly where things failed.140            // DEBUG: Write the grammar_str to test-grammar-integration.grammar.gbnf141            FILE* grammar_file = fopen("test-grammar-integration.grammar.gbnf", "w");142            if (grammar_file) {143                fprintf(grammar_file, "%s", grammar_str.c_str());144                fclose(grammar_file);145            }146 147            // DEBUG: Write the test string to test-grammar-integration.string.txt148            FILE* string_file = fopen("test-grammar-integration.string.txt", "w");149            if (string_file) {150                fprintf(string_file, "%s", test_string.c_str());151                fclose(string_file);152            }153 154            fprintf(stderr, "\n NOTE: Debug grammar file generated. To analyze this failure in detail, run the following command:     ./llama-gbnf-validator test-grammar-integration.grammar.gbnf test-grammar-integration.string.txt\n\n");155        } else {156            fprintf(stdout, "✅︎\n");157        }158 159        assert(matched);160 161        // Reset the grammar stacks162        stacks_cur = stacks_org;163    }164 165    fprintf(stderr, "  🟠 Invalid strings:\n");166 167    // Failing strings168    for (const auto & test_string : failing_strings) {169        fprintf(stderr, "    \"%s\" ", test_string.c_str());170        fflush(stderr);171 172        bool matched = match_string(test_string, grammar);173 174        if (matched) {175            fprintf(stderr, "❌ (incorrectly matched)\n");176        } else {177            fprintf(stdout, "✅︎\n");178        }179        assert(!matched);180 181        // Reset the grammar stacks182        stacks_cur = stacks_org;183    }184 185    // Clean up allocated memory186    llama_grammar_free_impl(grammar);187}188static void test_grammar(const std::string & test_desc, const std::string & grammar_str, const std::vector<std::string> & passing_strings, const std::vector<std::string> & failing_strings) {189    test(test_desc + ". Grammar: " + grammar_str, grammar_str, passing_strings, failing_strings);190}191static void test_schema(const std::string & test_desc, const std::string & schema_str, const std::vector<std::string> & passing_strings, const std::vector<std::string> & failing_strings) {192    test(test_desc + ". Schema: " + schema_str, json_schema_to_grammar(json::parse(schema_str), true), passing_strings, failing_strings);193}194 195static void test_simple_grammar() {196    test_schema(197        "min 0",198        R"""({199            "type": "integer",200            "minimum": 0201        })""",202        // Passing strings203        {204            "0",205            "10",206            "12",207            "10000",208        },209        // Failing strings210        {211            "-1",212            "-10",213            "-10000",214            "-100000000000000000000000000000000",215            "100000000000000000000000000000000",216            "00",217            "01",218            "-0",219        }220    );221    test_schema(222        "min 2",223        // Schema224        R"""({225            "type": "integer",226            "minimum": 2227        })""",228        // Passing strings229        {230            "2",231            "3",232            "4",233            "10",234            "20",235            "1234567890000000",236        },237        // Failing strings238        {239            "0",240            "1",241            "-1",242            "-100",243            "0",244            "1",245            "01",246            "02",247            "12345678900000000",248        }249    );250    test_schema(251        "min 456",252        R"""({253            "type": "integer",254            "minimum": 456255        })""",256        // Passing strings257        {258            "456",259            "4560",260            "457",261            "460",262            "500",263        },264        // Failing strings265        {266            "455",267            "356",268            "50",269            "050",270            "-1",271            "-456",272        }273    );274    test_schema(275        "min -123",276        R"""({277            "type": "integer",278            "minimum": -123279        })""",280        // Passing strings281        {282            "-123",283            "-122",284            "-11",285            "-1",286            "0",287            "1",288            "123",289            "1234",290            "2345",291        },292        // Failing strings293        {294            "-1234",295            "-124",296        }297    );298 299    test_schema(300        "max 9999",301        // Schema302        R"""({303            "type": "integer",304            "maximum": 9999305        })""",306        // Passing strings307        {308            "-99999",309            "0",310            "9999",311        },312        // Failing strings313        {314            "10000",315            "99991",316        }317    );318    test_schema(319        "max -9999",320        // Schema321        R"""({322            "type": "integer",323            "maximum": -9999324        })""",325        // Passing strings326        {327            "-10000",328            "-9999",329        },330        // Failing strings331        {332            "-9998",333            "0",334            "9999",335        }336    );337    test_schema(338        "min 5 max 30",339        // Schema340        R"""({341            "type": "integer",342            "minimum": 5,343            "maximum": 30344        })""",345        // Passing strings346        {347            "5",348            "10",349            "30",350        },351        // Failing strings352        {353            "05",354            "4",355            "-1",356            "31",357            "123",358            "0123",359        }360    );361    test_schema(362        "min 1 max 900719925474091",363        // Schema364        R"""({365            "type": "integer",366            "exclusiveMinimum": 0,367            "maximum": 900719925474091368        })""",369        // Passing strings370        {371            "1",372            "2",373            "10",374            "900719925474090",375            "900719925474091",376        },377        // Failing strings378        {379            "0",380            "01",381            "900719925474092",382            "9007199254740910",383        }384    );385    test_schema(386        "min -1 max 1",387        R"""({388            "type": "integer",389            "minimum": -1,390            "maximum": 1391        })""",392        // Passing strings393        {394            "-1",395            "0",396            "1",397        },398        // Failing strings399        {400            "-11",401            "-10",402            "-2",403            "2",404            "10",405            "11",406        }407    );408    test_schema(409        "min -123 max 42",410        R"""({411            "type": "integer",412            "minimum": -123,413            "maximum": 42414        })""",415        // Passing strings416        {417            "-123",418            "-122",419            "-13",420            "-11",421            "-2",422            "-1",423            "0",424            "1",425            "5",426            "10",427            "39",428            "40",429            "42",430        },431        // Failing strings432        {433            "-0123",434            "-124",435            "-1123",436            "-200",437            "43",438            "123",439            "0123",440        }441    );442    test_schema(443        "exclusive min / max",444        // Schema445        R"""({446            "type": "integer",447            "exclusiveMinimum": 0,448            "exclusiveMaximum": 10000449        })""",450        // Passing strings451        {452            "1",453            "9999",454        },455        // Failing strings456        {457            "0",458            "01",459            "10000",460            "99999",461        }462    );463 464    // Test case for a simple grammar465    test_grammar(466        "simple grammar",467        R"""(468            root ::= expr469            expr ::= term ("+" term)*470            term ::= number471            number ::= [0-9]+)""",472        // Passing strings473        {474            "42",475            "1+2+3+4+5",476            "123+456",477        },478        // Failing strings479        {480            "+",481            "/ 3",482            "1+2+3+4+5+",483            "12a45",484        }485    );486 487    // Test case for a simple grammar with tokens488    test_grammar(489        "simple grammar with tokens",490        R"""(491            root ::= <[10]> content <[11]>492            content ::= (!<[11]>)*)""",493        // Passing strings494        {495            token(10) + "hello world" + token(11),496            token(10) + "text with " + token(12) + " other tokens " + token(13) + " mixed in" + token(11),497            token(10) + token(11),498            token(10) + token(12) + token(13) + token(14) + token(15) + token(11),499            token(10) + "a" + token(11),500        },501        // Failing strings502        {503            token(10) + "missing end token",504            token(10),505            "missing start token" + token(11),506            token(10) + token(11) + token(11),  // double end token507            token(11) + "wrong order" + token(10),508        }509    );510}511 512static void test_complex_grammar() {513    // Test case for a more complex grammar, with both failure strings and success strings514    test_grammar(515        "medium complexity grammar",516        // Grammar517        R"""(518            root ::= expression519            expression ::= term ws (("+"|"-") ws term)*520            term ::= factor ws (("*"|"/") ws factor)*521            factor ::= number | variable | "(" expression ")" | function-call522            number ::= [0-9]+523            variable ::= [a-zA-Z_][a-zA-Z0-9_]*524            function-call ::= variable ws "(" (expression ("," ws expression)*)? ")"525            ws ::= [ \t\n\r]?)""",526        // Passing strings527        {528            "42",529            "1*2*3*4*5",530            "x",531            "x+10",532            "x1+y2",533            "(a+b)*(c-d)",534            "func()",535            "func(x,y+2)",536            "a*(b+c)-d/e",537            "f(g(x),h(y,z))",538            "x + 10",539            "x1 + y2",540            "(a + b) * (c - d)",541            "func()",542            "func(x, y + 2)",543            "a * (b + c) - d / e",544            "f(g(x), h(y, z))",545            "123+456",546            "123*456*789-123/456+789*123",547            "123+456*789-123/456+789*123-456/789+123*456-789/123+456*789-123/456+789*123-456"548        },549        // Failing strings550        {551            "+",552            "/ 3x",553            "x + + y",554            "a * / b",555            "func(,)",556            "func(x y)",557            "(a + b",558            "x + y)",559            "a + b * (c - d",560            "42 +",561            "x +",562            "x + 10 +",563            "(a + b) * (c - d",564            "func(",565            "func(x, y + 2",566            "a * (b + c) - d /",567            "f(g(x), h(y, z)",568            "123+456*789-123/456+789*123-456/789+123*456-789/123+456*789-123/456+789*123-456/",569        }570    );571 572    // Test case for a more complex grammar with tokens573    test_grammar(574        "complex grammar with tokens",575        R"""(576            root ::= reasoning+ content tool-call*577            reasoning ::= <[10]> (!<[11]>)* <[11]>578            content ::= <[20]> (!<[21]>)* <[21]>579            tool-call ::= <[12]> name <[13]> args <[14]>580            name ::= (!<[13]>)+581            args ::= (!<[14]>)*)""",582        // Passing strings583        {584            token(10) + "I am thinking" + token(11) + token(20) + "hello world!" + token(21) + token(12) + "search" + token(13) + "query=test" + token(14),585            token(10) + "reasoning 1" + token(11) + token(10) + "reasoning 2" + token(11) + token(20) + token(21) + token(12) + "tool" + token(13) + token(14),586            token(10) + token(11) + token(20) + "content" + token(21),587            token(10) + "think" + token(12) + " nested" + token(11) + token(20) + token(10) + "more content" + token(21) + token(12) + "fn" + token(13) + "x=1,y=2" + token(14) + token(12) + "fn2" + token(13) + token(14),588            token(10) + "reasoning" + token(11) + token(10) + "more" + token(11) + token(10) + "even more" + token(11) + token(20) + "text" + token(21) + token(12) + "a" + token(13) + "b" + token(14) + token(12) + "c" + token(13) + "d" + token(14),589        },590        // Failing strings591        {592            token(20) + "content only" + token(21),593            token(10) + "no closing reasoning",594            token(10) + token(11) + token(20) + "no closing content",595            token(10) + token(11) + token(20) + token(21) + token(12) + "incomplete tool",596            token(10) + token(11) + token(11) + token(20) + token(21),597        }598    );599}600 601static void test_special_chars() {602    // A collection of tests to exercise special characters such as "."603    test_grammar(604        "special characters",605        // Grammar606        R"""(607            root ::= ... "abc" ...608            )""",609        // Passing strings610        {611            "abcabcabc",612            "aaaabcccc",613            // NOTE: Also ensures that multi-byte characters still count as a single character614            "🔵🟠✅abc❌🟠🔵"615        },616        // Failing strings617        {618            "aaabcccc",619            "aaaaabcccc",620            "aaaabccc",621            "aaaabccccc",622            "🔵🟠✅❌abc❌✅🟠🔵",623            "🔵🟠abc🟠🔵"624        }625    );626}627 628static void test_quantifiers() {629    // A collection of tests to exercise * + and ? quantifiers630 631    test_grammar(632        "* quantifier",633        // Grammar634        R"""(root ::= "a"*)""",635        // Passing strings636        {637            "",638            "a",639            "aaaaa",640            "aaaaaaaaaaaaaaaaaa",641            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"642        },643        // Failing strings644        {645            "b",646            "ab",647            "aab",648            "ba",649            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"650        }651    );652    test_grammar(653        "+ quantifier",654        // Grammar655        R"""(root ::= "a"+)""",656        // Passing strings657        {658            "a",659            "aaaaa",660            "aaaaaaaaaaaaaaaaaa",661            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"662        },663        // Failing strings664        {665            "",666            "b",667            "ab",668            "aab",669            "ba",670            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"671        }672    );673    test_grammar(674        "? quantifier",675        // Grammar676        R"""(root ::= "a"?)""",677        // Passing strings678        {679            "",680            "a"681        },682        // Failing strings683        {684            "b",685            "ab",686            "aa",687            "ba",688        }689    );690    test_grammar(691        "mixed quantifiers",692        // Grammar693        R"""(694            root ::= cons+ vowel* cons? (vowel cons)*695            vowel ::= [aeiouy]696            cons ::= [bcdfghjklmnpqrstvwxyz]697            )""",698        // Passing strings699        {700            "yes",701            "no",702            "noyes",703            "crwth",704            "four",705            "bryyyy",706        },707        // Failing strings708        {709            "yess",710            "yesno",711            "forty",712            "catyyy",713        }714    );715    test_grammar(716        "simple exact repetition",717        // Grammar718        R"""(719            root ::= [ab]{4}720        )""",721        // Passing strings722        {723            "aaaa",724            "bbbb",725            "abab",726        },727        // Failing strings728        {729            "a",730            "b",731            "aaaaa",732        }733    );734    test_grammar(735        "simple min repetition",736        // Grammar737        R"""(738            root ::= [ab]{4,}739        )""",740        // Passing strings741        {742            "aaaa",743            "aaaaab",744            "bbbb",745            "ababab",746        },747        // Failing strings748        {749            "",750            "aba",751        }752    );753    test_grammar(754        "simple max repetition",755        // Grammar756        R"""(757            root ::= [ab]{0,4}758        )""",759        // Passing strings760        {761            "",762            "a",763            "aa",764            "aaa",765            "aaab",766        },767        // Failing strings768        {769            "aaaaa",770        }771    );772    test_grammar(773        "min / max repetition",774        // Grammar775        R"""(776            root ::= ("0x" [A-F0-9]{2} " "?){3,5}777        )""",778        // Passing strings779        {780            "0xFF 0x12 0xAB",781            "0xFF 0x12 0xAB 0x00 0x00",782        },783        // Failing strings784        {785            "",786            "0xFF",787            "0xFF 0x12",788            "0xFF 0x12 0xAB 0x00 0x00 0x00",789        }790    );791    test_grammar(792        "segfault",793        // Grammar794        R"""(795            root ::= ( [x]* )*796        )""",797        // Passing strings798        {799            "",800            "x",801            "xx"802        },803        // Failing strings804        {805            "y",806            "yy"807        }808    );809}810 811static void test_failure_missing_root() {812    fprintf(stderr, "⚫ Testing missing root node:\n");813    // Test case for a grammar that is missing a root rule814    const std::string grammar_str = R"""(815        rot ::= expr816        expr ::= term ("+" term)*817        term ::= number818        number ::= [0-9]+)""";819 820    llama_grammar_parser parsed_grammar;821    parsed_grammar.parse(grammar_str.c_str());822 823    // Ensure we parsed correctly824    assert(!parsed_grammar.rules.empty());825 826    // Ensure we do NOT have a root node827    assert(parsed_grammar.symbol_ids.find("root") == parsed_grammar.symbol_ids.end());828    fprintf(stderr, "  ✅︎ Passed\n");829}830 831static void test_failure_missing_reference() {832    fprintf(stderr, "⚫ Testing missing reference node:\n");833 834    // Test case for a grammar that is missing a referenced rule835    const std::string grammar_str =836        R"""(root ::= expr837        expr ::= term ("+" term)*838        term ::= numero839        number ::= [0-9]+)""";840 841    fprintf(stderr, "    Expected error:  ");842 843    llama_grammar_parser parsed_grammar;844    parsed_grammar.parse(grammar_str.c_str());845 846    // Ensure we did NOT parsed correctly847    assert(parsed_grammar.rules.empty());848 849    fprintf(stderr, "    End of expected error.\n");850    fprintf(stderr, "  ✅︎ Passed\n");851}852 853static void test_failure_left_recursion() {854    fprintf(stderr, "⚫ Testing left recursion detection:\n");855 856    // Test simple left recursion detection857    const std::string simple_str = R"""(root ::= "a" | root "a")""";858    assert(test_build_grammar_fails(simple_str));859 860    // Test more complicated left recursion detection861    const std::string medium_str = R"""(862        root ::= asdf863        asdf ::= "a" | asdf "a"864        )""";865    assert(test_build_grammar_fails(medium_str));866 867    // Test even more complicated left recursion detection868    const std::string hard_str = R"""(869        root ::= asdf870        asdf ::= "a" | foo "b"871        foo ::= "c" | asdf "d" | "e")""";872    assert(test_build_grammar_fails(hard_str));873 874    // Test yet even more complicated left recursion detection875    const std::string hardest_str = R"""(876        root ::= asdf877        asdf ::= "a" | foo "b"878        foo ::= "c" | empty asdf "d" | "e"879        empty ::= "blah" | )""";880    assert(test_build_grammar_fails(hardest_str));881 882    fprintf(stderr, "  ✅︎ Passed\n");883}884 885static void test_failure_missing_root_symbol() {886    fprintf(stderr, "⚫ Testing missing root symbol:\n");887 888    const std::string grammar_str = R"""(889        root ::= "foobar"890    )""";891 892    llama_grammar * failure_result = build_grammar_with_root(grammar_str, "nonexistent");893    assert(failure_result == nullptr);894 895    fprintf(stderr, "  ✅︎ Passed\n");896}897 898static void test_custom_root_symbol_check() {899    fprintf(stderr, "⚫ Testing custom root symbol check:\n");900 901    const std::string custom_root_grammar_str = R"""(902        foobar ::= "foobar"903    )""";904 905    llama_grammar * failure_result = build_grammar_with_root(custom_root_grammar_str, "root");906    assert(failure_result == nullptr);907 908    llama_grammar * success_result = build_grammar_with_root(custom_root_grammar_str, "foobar");909    assert(success_result != nullptr);910    llama_grammar_free_impl(success_result);911 912    fprintf(stderr, "  ✅︎ Passed\n");913}914 915static void test_json_schema() {916    // Note that this is similar to the regular grammar tests,917    //  but we convert each json schema to a grammar before parsing.918    // Otherwise, this test structure is the same.919 920    test_schema(921        "empty schema (any value)",922        // Schema923        R"""(924            {}925        )""",926        // Passing strings927        {928            R"""({})""",929            R"""({"foo": "bar"})""",930            "[]",931            "null",932            R"""("")""",933            "true",934        },935        // Failing strings936        {937            "",938            R"""({"foo"})""",939            "foo",940        }941    );942 943    test_schema(944        "exotic formats (list)",945        // Schema946        R"""({947            "items": [948                { "format": "date" },949                { "format": "uuid" },950                { "format": "time" },951                { "format": "date-time" }952            ]953        })""",954        // Passing strings955        {956            // "{}", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it?957            // "[]", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it?958            R"""(["2012-04-23", "12345678-1234-1234-1234-1234567890ab", "18:25:43.511Z", "2012-04-23T18:25:43.511Z"])""",959            //R"""(["2012-04-23","12345678-1234-1234-1234-1234567890ab"])""", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it?960            //R"""({"foo": "bar"})""", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it?961        },962        // Failing strings963        {964            R"""(["foo", "bar"])""",965            R"""(["12345678-1234-1234-1234-1234567890ab"])""",966        }967    );968 969    test_schema(970        "string",971        // Schema972        R"""({973            "type": "string"974        })""",975        // Passing strings976        {977            R"""("foo")""",978            R"""("bar")""",979            R"""("")""",980        },981        // Failing strings982        {983            R"""({})""",984            R"""("foo": "bar")""",985        }986    );987 988    test_schema(989        "string w/ min length 1",990        // Schema991        R"""({992            "type": "string",993            "minLength": 1994        })""",995        // Passing strings996        {997            R"""("foo")""",998            R"""("bar")""",999        },1000        // Failing strings1001        {1002            R"""("")""",1003            R"""({})""",1004            R"""("foo": "bar")""",1005        }1006    );1007 1008    test_schema(1009        "string w/ min length 3",1010        // Schema1011        R"""({1012                "type": "string",1013                "minLength": 31014        })""",1015        // Passing strings1016        {1017            R"""("foo")""",1018            R"""("bar")""",1019            R"""("foobar")""",1020        },1021        // Failing strings1022        {1023            R"""("")""",1024            R"""("f")""",1025            R"""("fo")""",1026        }1027    );1028 1029    test_schema(1030        "string w/ max length",1031        // Schema1032        R"""({1033            "type": "string",1034            "maxLength": 31035        })""",1036        // Passing strings1037        {1038            R"""("foo")""",1039            R"""("bar")""",1040            R"""("")""",1041            R"""("f")""",1042            R"""("fo")""",1043        },1044        // Failing strings1045        {1046            R"""("foobar")""",1047        }1048    );1049 1050    test_schema(1051        "string w/ min & max length",1052        // Schema1053        R"""({1054            "type": "string",1055            "minLength": 1,1056            "maxLength": 41057        })""",1058        // Passing strings1059        {1060            R"""("foo")""",1061            R"""("bar")""",1062            R"""("f")""",1063            R"""("barf")""",1064        },1065        // Failing strings1066        {1067            R"""("")""",1068            R"""("barfo")""",1069            R"""("foobar")""",1070        }1071    );1072 1073    test_schema(1074        "boolean",1075        // Schema1076        R"""({1077            "type": "boolean"1078        })""",1079        // Passing strings1080        {1081            "true",1082            "false",1083        },1084        // Failing strings1085        {1086            R"""("")""",1087            R"""("true")""",1088            R"""(True)""",1089            R"""(FALSE)""",1090        }1091    );1092 1093    test_schema(1094        "integer",1095        // Schema1096        R"""({1097            "type": "integer"1098        })""",1099        // Passing strings1100        {1101            R"""(0)""",1102            R"""(12345)""",1103            R"""(1234567890123456)""",1104        },1105        // Failing strings1106        {1107            R"""()""",1108            R"""(01)""",1109            R"""(007)""",1110            R"""(12345678901234567  )""",1111        }1112    );1113 1114    test_schema(1115        "string const",1116        // Schema1117        R"""({1118            "const": "foo"1119        })""",1120        // Passing strings1121        {1122            R"""("foo")""",1123        },1124        // Failing strings1125        {1126            R"""(foo)""",1127            R"""("bar")""",1128        }1129    );1130 1131    test_schema(1132        "non-string const",1133        // Schema1134        R"""({1135            "const": true1136        })""",1137        // Passing strings1138        {1139            R"""(true)""",1140        },1141        // Failing strings1142        {1143            R"""()""",1144            R"""(foo)""",1145            R"""("true")""",1146        }1147    );1148 1149    test_schema(1150        "non-string const",1151        // Schema1152        R"""({1153            "enum": ["red", "amber", "green", null, 42, ["foo"]]1154        })""",1155        // Passing strings1156        {1157            R"""("red")""",1158            R"""(null)""",1159            R"""(42)""",1160            R"""(["foo"])""",1161        },1162        // Failing strings1163        {1164            R"""()""",1165            R"""(420)""",1166            R"""(true)""",1167            R"""(foo)""",1168        }1169    );1170 1171    test_schema(1172        "simple pattern",1173        // Schema1174        R"""({1175            "pattern": "^[a-zA-Z0-9_-]*$"1176        })""",1177        // Passing strings1178        {1179            R"""("")""",1180            R"""("He_llo-12")""",1181        },1182        // Failing strings1183        {1184            R"""("!")""",1185            R"""("Hello World")""",1186        }1187    );1188 1189    test_schema(1190        "pattern with escapes",1191        // Schema1192        R"""({1193            "pattern": "^a\\^\\$\\.\\[\\]\\(\\)\\|\\{\\}\\*\\+\\?b$"1194        })""",1195        // Passing strings1196        {1197            R"""("a^$.[]()|{}*+?b")""",1198        },1199        // Failing strings1200        {

Showing the first 1,200 of 1496 lines. Download the file for the rest.