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: 150 150 100.0 %
Date: 2024-09-28 11:33:24 Functions: 18 19 94.7 %
Branches: 24 40 60.0 %

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

Generated by: LCOV version 1.14