LCOV - code coverage report
Current view: top level - buildbot/coverage/build/test/unit/parser - parser_black.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 168 168 100.0 %
Date: 2026-09-17 10:08:19 Functions: 30 31 96.8 %
Branches: 33 58 56.9 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * Black box testing of cvc5::parser::InputParser for SMT-LIbv2 inputs.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include <cvc5/cvc5.h>
      14                 :            : #include <cvc5/cvc5_parser.h>
      15                 :            : 
      16                 :            : #include <sstream>
      17                 :            : 
      18                 :            : #include "base/output.h"
      19                 :            : #include "options/base_options.h"
      20                 :            : #include "options/language.h"
      21                 :            : #include "options/options.h"
      22                 :            : #include "test.h"
      23                 :            : 
      24                 :            : using namespace cvc5::parser;
      25                 :            : 
      26                 :            : namespace cvc5::internal {
      27                 :            : namespace test {
      28                 :            : 
      29                 :            : class TestParserBlack : public TestInternal
      30                 :            : {
      31                 :            :  protected:
      32                 :          5 :   TestParserBlack(modes::InputLanguage lang) : d_lang(lang) {}
      33                 :            : 
      34                 :          5 :   virtual ~TestParserBlack() {}
      35                 :            : 
      36                 :          5 :   void SetUp() override
      37                 :            :   {
      38                 :          5 :     TestInternal::SetUp();
      39                 :          5 :     d_symman.reset(nullptr);
      40                 :          5 :     d_solver.reset(new cvc5::Solver(d_tm));
      41                 :          5 :     d_solver->setOption("parse-only", "true");
      42                 :          5 :   }
      43                 :            : 
      44                 :          5 :   void TearDown() override
      45                 :            :   {
      46                 :          5 :     d_symman.reset(nullptr);
      47                 :          5 :     d_solver.reset(nullptr);
      48                 :          5 :   }
      49                 :            : 
      50                 :            :   /* Set up declaration context for expr inputs */
      51                 :         33 :   void setupContext(InputParser& parser)
      52                 :            :   {
      53                 :         33 :     std::stringstream ss;
      54                 :         33 :     ss << "(set-logic ALL)" << std::endl;
      55                 :         33 :     ss << "(declare-fun a () Bool)" << std::endl;
      56                 :         33 :     ss << "(declare-fun b () Bool)" << std::endl;
      57                 :         33 :     ss << "(declare-fun c () Bool)" << std::endl;
      58                 :         33 :     ss << "(declare-sort t 0)" << std::endl;
      59                 :         33 :     ss << "(declare-sort u 0)" << std::endl;
      60                 :         33 :     ss << "(declare-sort v 0)" << std::endl;
      61                 :         33 :     ss << "(declare-fun f (t) u)" << std::endl;
      62                 :         33 :     ss << "(declare-fun g (u) v)" << std::endl;
      63                 :         33 :     ss << "(declare-fun h (v) t)" << std::endl;
      64                 :         33 :     ss << "(declare-fun x () t)" << std::endl;
      65                 :         33 :     ss << "(declare-fun y () u)" << std::endl;
      66                 :         33 :     ss << "(declare-fun z () v)" << std::endl;
      67                 :         33 :     parser.setStreamInput(
      68                 :            :         modes::InputLanguage::SMT_LIB_2_6, ss, "parser_black");
      69                 :         33 :     Command cmd;
      70                 :         33 :     std::stringstream tmp;
      71                 :            :     while (true)
      72                 :            :     {
      73                 :        462 :       cmd = parser.nextCommand();
      74         [ +  + ]:        462 :       if (cmd.isNull())
      75                 :            :       {
      76                 :         33 :         break;
      77                 :            :       }
      78                 :        429 :       cmd.invoke(d_solver.get(), d_symman.get(), tmp);
      79                 :            :     }
      80                 :         33 :   }
      81                 :            : 
      82                 :         14 :   void tryGoodInput(const std::string goodInput)
      83                 :            :   {
      84                 :         14 :     d_solver.reset(new cvc5::Solver(d_tm));
      85                 :         14 :     d_symman.reset(new SymbolManager(d_tm));
      86                 :         14 :     InputParser parser(d_solver.get(), d_symman.get());
      87                 :         14 :     std::stringstream ss;
      88                 :         14 :     ss << goodInput;
      89                 :         14 :     parser.setStreamInput(
      90                 :            :         modes::InputLanguage::SMT_LIB_2_6, ss, "parser_black");
      91 [ -  + ][ +  - ]:         14 :     ASSERT_FALSE(parser.done());
      92                 :         14 :     Command cmd;
      93                 :         14 :     std::stringstream tmp;
      94                 :            :     while (true)
      95                 :            :     {
      96                 :         44 :       cmd = parser.nextCommand();
      97         [ +  + ]:         44 :       if (cmd.isNull())
      98                 :            :       {
      99                 :         14 :         break;
     100                 :            :       }
     101         [ +  - ]:         30 :       Trace("parser") << "Parsed command: " << cmd << std::endl;
     102                 :         30 :       cmd.invoke(d_solver.get(), d_symman.get(), tmp);
     103                 :            :     }
     104                 :            : 
     105 [ -  + ][ +  - ]:         14 :     ASSERT_TRUE(parser.done());
     106 [ +  - ][ +  - ]:         14 :   }
     107                 :            : 
     108                 :          8 :   void tryBadInput(const std::string badInput, bool strictMode = false)
     109                 :            :   {
     110                 :          8 :     d_solver.reset(new cvc5::Solver(d_tm));
     111         [ +  + ]:          8 :     d_solver->setOption("strict-parsing", strictMode ? "true" : "false");
     112                 :          8 :     d_symman.reset(new SymbolManager(d_tm));
     113                 :          8 :     InputParser parser(d_solver.get(), d_symman.get());
     114                 :          8 :     std::stringstream ss;
     115                 :          8 :     ss << badInput;
     116                 :          8 :     parser.setStreamInput(d_lang, ss, "parser_black");
     117                 :         27 :     ASSERT_THROW(
     118                 :            :         {
     119                 :            :           Command cmd;
     120                 :            :           std::stringstream tmp;
     121                 :            :           while (true)
     122                 :            :           {
     123                 :            :             cmd = parser.nextCommand();
     124                 :            :             if (cmd.isNull())
     125                 :            :             {
     126                 :            :               break;
     127                 :            :             }
     128                 :            :             Trace("parser") << "Parsed command: " << cmd << std::endl;
     129                 :            :             cmd.invoke(d_solver.get(), d_symman.get(), tmp);
     130                 :            :           }
     131                 :            :           std::cout << "\nBad input succeeded:\n" << badInput << std::endl;
     132                 :            :         },
     133         [ +  - ]:          8 :         ParserException);
     134 [ +  - ][ +  - ]:          8 :   }
     135                 :            : 
     136                 :         12 :   void tryGoodExpr(const std::string goodExpr)
     137                 :            :   {
     138                 :         12 :     d_solver.reset(new cvc5::Solver(d_tm));
     139                 :         12 :     d_symman.reset(new SymbolManager(d_tm));
     140                 :         12 :     InputParser parser(d_solver.get(), d_symman.get());
     141                 :         12 :     setupContext(parser);
     142                 :            : 
     143                 :         12 :     std::stringstream ss;
     144                 :         12 :     ss << goodExpr;
     145                 :         12 :     parser.setStreamInput(d_lang, ss, "parser_black");
     146                 :            : 
     147 [ -  + ][ +  - ]:         12 :     ASSERT_FALSE(parser.done());
     148                 :         12 :     cvc5::Term e = parser.nextTerm();
     149 [ -  + ][ +  - ]:         12 :     ASSERT_FALSE(e.isNull());
     150                 :         12 :     e = parser.nextTerm();
     151 [ -  + ][ +  - ]:         12 :     ASSERT_TRUE(parser.done());
     152 [ -  + ][ +  - ]:         12 :     ASSERT_TRUE(e.isNull());
     153 [ +  - ][ +  - ]:         12 :   }
     154                 :            : 
     155                 :            :   /**
     156                 :            :    * NOTE: The check implemented here may fail if a bad expression
     157                 :            :    * expression string has a prefix that is parseable as a good
     158                 :            :    * expression. E.g., the bad SMT v2 expression "#b10@@@@@@" will
     159                 :            :    * actually return the bit-vector 10 and ignore the tail of the
     160                 :            :    * input. It's more trouble than it's worth to check that the whole
     161                 :            :    * input was consumed here, so just be careful to avoid valid
     162                 :            :    * prefixes in tests.
     163                 :            :    */
     164                 :         21 :   void tryBadExpr(const std::string badExpr, bool strictMode = false)
     165                 :            :   {
     166                 :         21 :     d_solver.reset(new cvc5::Solver(d_tm));
     167         [ +  + ]:         21 :     d_solver->setOption("strict-parsing", strictMode ? "true" : "false");
     168                 :         21 :     d_symman.reset(new SymbolManager(d_tm));
     169                 :         21 :     InputParser parser(d_solver.get(), d_symman.get());
     170                 :         21 :     setupContext(parser);
     171                 :         21 :     std::stringstream ss;
     172                 :         21 :     ss << badExpr;
     173                 :         21 :     parser.setStreamInput(d_lang, ss, "parser_black");
     174 [ -  + ][ +  - ]:         21 :     ASSERT_FALSE(parser.done());
     175                 :         42 :     ASSERT_THROW(cvc5::Term e = parser.nextTerm();
     176                 :            :                  std::cout << std::endl
     177                 :            :                            << "Bad expr succeeded." << std::endl
     178                 :            :                            << "Input: <<" << badExpr << ">>" << std::endl
     179                 :            :                            << "Output: <<" << e << ">>" << std::endl;
     180         [ +  - ]:         21 :                  , ParserException);
     181 [ +  - ][ +  - ]:         21 :   }
     182                 :            : 
     183                 :            :   modes::InputLanguage d_lang;
     184                 :            :   cvc5::TermManager d_tm;
     185                 :            :   std::unique_ptr<cvc5::Solver> d_solver;
     186                 :            :   std::unique_ptr<SymbolManager> d_symman;
     187                 :            : };
     188                 :            : 
     189                 :            : /* -------------------------------------------------------------------------- */
     190                 :            : 
     191                 :            : class TestParserBlackSmt2InputParser : public TestParserBlack
     192                 :            : {
     193                 :            :  protected:
     194                 :          5 :   TestParserBlackSmt2InputParser()
     195                 :          5 :       : TestParserBlack(modes::InputLanguage::SMT_LIB_2_6)
     196                 :            :   {
     197                 :          5 :   }
     198                 :            : };
     199                 :            : 
     200                 :          4 : TEST_F(TestParserBlackSmt2InputParser, good_inputs)
     201                 :            : {
     202                 :          1 :   tryGoodInput("");  // empty string is OK
     203                 :          1 :   tryGoodInput("(set-logic QF_UF)");
     204                 :          1 :   tryGoodInput("(set-info :notes |This is a note, take note!|)");
     205                 :          1 :   tryGoodInput("(set-logic QF_UF) (assert true)");
     206                 :          1 :   tryGoodInput("(check-sat)");
     207                 :          1 :   tryGoodInput("(exit)");
     208                 :          1 :   tryGoodInput("(set-logic QF_UF) (assert false) (check-sat)");
     209                 :          1 :   tryGoodInput(
     210                 :            :       "(set-logic QF_UF) (declare-fun a () Bool) "
     211                 :            :       "(declare-fun b () Bool)");
     212                 :          1 :   tryGoodInput(
     213                 :            :       "(set-logic QF_UF) (declare-fun a () Bool) "
     214                 :            :       "(declare-fun b () Bool) (assert (=> (and (=> a b) a) b))");
     215                 :          1 :   tryGoodInput(
     216                 :            :       "(set-logic QF_UF) (declare-sort a 0) "
     217                 :            :       "(declare-fun f (a) a) (declare-fun x () a) "
     218                 :            :       "(assert (= (f x) x))");
     219                 :          1 :   tryGoodInput(
     220                 :            :       "(set-logic QF_UF) (declare-sort a 0) "
     221                 :            :       "(declare-fun x () a) (declare-fun y () a) "
     222                 :            :       "(assert (= (ite true x y) x))");
     223                 :          1 :   tryGoodInput(";; nothing but a comment");
     224                 :          1 :   tryGoodInput("; a comment\n(check-sat ; goodbye\n)");
     225                 :          1 : }
     226                 :            : 
     227                 :          4 : TEST_F(TestParserBlackSmt2InputParser, bad_inputs)
     228                 :            : {
     229                 :            :   // competition builds don't do any checking
     230                 :            : #ifndef CVC5_COMPETITION_MODE
     231                 :            :   // no arguments
     232                 :          1 :   tryBadInput("(assert)");
     233                 :            :   // illegal character in symbol
     234                 :          1 :   tryBadInput("(set-info :notes |Symbols can't contain the | character|)");
     235                 :            :   // check-sat should not have an argument
     236                 :          1 :   tryBadInput("(set-logic QF_UF) (check-sat true)", true);
     237                 :            :   // no argument
     238                 :          1 :   tryBadInput("(declare-sort a)");
     239                 :            :   // double declaration
     240                 :          1 :   tryBadInput("(declare-sort a 0) (declare-sort a 0)");
     241                 :            :   // should be "(declare-fun p () Bool)"
     242                 :          1 :   tryBadInput("(set-logic QF_UF) (declare-fun p Bool)");
     243                 :            :   // strict mode
     244                 :            :   // no set-logic, core theory symbol "true" undefined
     245                 :          1 :   tryBadInput("(assert true)", true);
     246                 :            :   // core theory symbol "Bool" undefined
     247                 :          1 :   tryBadInput("(declare-fun p Bool)", true);
     248                 :            : #endif
     249                 :          1 : }
     250                 :            : 
     251                 :          4 : TEST_F(TestParserBlackSmt2InputParser, ff_byte_not_eof)
     252                 :            : {
     253                 :          1 :   std::string ffByte(1, static_cast<char>(0xFF));
     254                 :          1 :   std::string input = "(set-logic QF_UF)\n";
     255                 :          1 :   input += "(set-info :notes |ff";
     256                 :          1 :   input += ffByte;
     257                 :          1 :   input += "name|)\n";
     258                 :          1 :   input += "; comment with ";
     259                 :          1 :   input += ffByte;
     260                 :          1 :   input += " byte here\n";
     261                 :          1 :   input += "(check-sat)\n";
     262                 :          1 :   tryGoodInput(input);
     263                 :          1 : }
     264                 :            : 
     265                 :          4 : TEST_F(TestParserBlackSmt2InputParser, good_exprs)
     266                 :            : {
     267                 :          1 :   tryGoodExpr("(and a b)");
     268                 :          1 :   tryGoodExpr("(or (and a b) c)");
     269                 :          1 :   tryGoodExpr("(=> (and (=> a b) a) b)");
     270                 :          1 :   tryGoodExpr("(and (= a b) (not a))");
     271                 :          1 :   tryGoodExpr("(= (xor a b) (and (or a b) (not (and a b))))");
     272                 :          1 :   tryGoodExpr("(ite a (f x) y)");
     273                 :          1 :   tryGoodExpr("1");
     274                 :          1 :   tryGoodExpr("0");
     275                 :          1 :   tryGoodExpr("1.5");
     276                 :          1 :   tryGoodExpr("#xfab09c7");
     277                 :          1 :   tryGoodExpr("#b0001011");
     278                 :          1 :   tryGoodExpr("(* 5 1)");
     279                 :          1 : }
     280                 :            : 
     281                 :          4 : TEST_F(TestParserBlackSmt2InputParser, bad_exprs)
     282                 :            : {
     283                 :            : // competition builds don't do any checking
     284                 :            : #ifndef CVC5_COMPETITION_MODE
     285                 :          1 :   tryBadExpr("(and)");                     // wrong arity
     286                 :          1 :   tryBadExpr("(and a b");                  // no closing paren
     287                 :          1 :   tryBadExpr("(a and b)");                 // infix
     288                 :          1 :   tryBadExpr("(implies a b)");             // no implies in v2
     289                 :          1 :   tryBadExpr("(iff a b)");                 // no iff in v2
     290                 :          1 :   tryBadExpr("(OR (AND a b) c)");          // wrong case
     291                 :          1 :   tryBadExpr("(a IMPLIES b)");             // infix AND wrong case
     292                 :          1 :   tryBadExpr("(not a b)");                 // wrong arity
     293                 :          1 :   tryBadExpr("not a");                     // needs parens
     294                 :          1 :   tryBadExpr("(ite a x)");                 // wrong arity
     295                 :          1 :   tryBadExpr("(if_then_else a (f x) y)");  // no if_then_else in v2
     296                 :          1 :   tryBadExpr("(a b)");                     // using non-function as function
     297                 :          1 :   tryBadExpr(".5");  // rational constants must have integer prefix
     298                 :          1 :   tryBadExpr("1.");  // rational constants must have fractional suffix
     299                 :          1 :   tryBadExpr("#x");  // hex constants must have at least one digit
     300                 :          1 :   tryBadExpr("#b");  // ditto binary constants
     301                 :          1 :   tryBadExpr("#xg0f");
     302                 :          1 :   tryBadExpr("#b9");
     303                 :            :   // Bad strict exprs
     304                 :          1 :   tryBadExpr("(and a)", true);   // no unary and's
     305                 :          1 :   tryBadExpr("(or a)", true);    // no unary or's
     306                 :          1 :   tryBadExpr("(* 5 01)", true);  // '01' is not a valid integer constant
     307                 :            : #endif
     308                 :          1 : }
     309                 :            : }  // namespace test
     310                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14