// Copyright 2008 The RE2 Authors.  All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#ifndef RE2_TESTING_TESTER_H_
#define RE2_TESTING_TESTER_H_

// Comparative tester for regular expression matching.
// Checks all implementations against each other.

#include <vector>

#include "re2/stringpiece.h"
#include "re2/prog.h"
#include "re2/regexp.h"
#include "re2/re2.h"
#include "util/pcre.h"

namespace re2 {

// All the supported regexp engines.
enum Engine {
  kEngineBacktrack = 0,    // Prog::UnsafeSearchBacktrack
  kEngineNFA,              // Prog::SearchNFA
  kEngineDFA,              // Prog::SearchDFA, only ask whether it matched
  kEngineDFA1,             // Prog::SearchDFA, ask for match[0]
  kEngineOnePass,          // Prog::SearchOnePass, if applicable
  kEngineBitState,         // Prog::SearchBitState
  kEngineRE2,              // RE2, all submatches
  kEngineRE2a,             // RE2, only ask for match[0]
  kEngineRE2b,             // RE2, only ask whether it matched
  kEnginePCRE,             // PCRE (util/pcre.h)

  kEngineMax,
};

// Make normal math on the enum preserve the type.
// By default, C++ doesn't define ++ on enum, and e+1 has type int.
static inline void operator++(Engine& e, int unused) {
  e = static_cast<Engine>(e+1);
}

static inline Engine operator+(Engine e, int i) {
  return static_cast<Engine>(static_cast<int>(e)+i);
}

// A TestInstance caches per-regexp state for a given
// regular expression in a given configuration
// (UTF-8 vs Latin1, longest vs first match, etc.).
class TestInstance {
 public:
  struct Result;

  TestInstance(const StringPiece& regexp, Prog::MatchKind kind,
               Regexp::ParseFlags flags);
  ~TestInstance();
  Regexp::ParseFlags flags() { return flags_; }
  bool error() { return error_; }

  // Runs a single test case: search in text, which is in context,
  // using the given anchoring.
  bool RunCase(const StringPiece& text, const StringPiece& context,
               Prog::Anchor anchor);

 private:
  // Runs a single search using the named engine type.
  void RunSearch(Engine type,
                 const StringPiece& text, const StringPiece& context,
                 Prog::Anchor anchor,
                 Result *result);

  void LogMatch(const char* prefix, Engine e, const StringPiece& text,
                const StringPiece& context, Prog::Anchor anchor);

  const StringPiece regexp_str_;    // regexp being tested
  Prog::MatchKind kind_;            // kind of match
  Regexp::ParseFlags flags_;        // flags for parsing regexp_str_
  bool error_;                      // error during constructor?

  Regexp* regexp_;                  // parsed regexp
  int num_captures_;                // regexp_->NumCaptures() cached
  Prog* prog_;                      // compiled program
  Prog* rprog_;                     // compiled reverse program
  PCRE* re_;                        // PCRE implementation
  RE2* re2_;                        // RE2 implementation

  TestInstance(const TestInstance&) = delete;
  TestInstance& operator=(const TestInstance&) = delete;
};

// A group of TestInstances for all possible configurations.
class Tester {
 public:
  explicit Tester(const StringPiece& regexp);
  ~Tester();

  bool error() { return error_; }

  // Runs a single test case: search in text, which is in context,
  // using the given anchoring.
  bool TestCase(const StringPiece& text, const StringPiece& context,
                Prog::Anchor anchor);

  // Run TestCase(text, text, anchor) for all anchoring modes.
  bool TestInput(const StringPiece& text);

  // Run TestCase(text, context, anchor) for all anchoring modes.
  bool TestInputInContext(const StringPiece& text, const StringPiece& context);

 private:
  bool error_;
  std::vector<TestInstance*> v_;

  Tester(const Tester&) = delete;
  Tester& operator=(const Tester&) = delete;
};

// Run all possible tests using regexp and text.
bool TestRegexpOnText(const StringPiece& regexp, const StringPiece& text);

}  // namespace re2

#endif  // RE2_TESTING_TESTER_H_
