Checksum.hh 5.13 KB
Newer Older
Martin Marinov's avatar
Martin Marinov committed
1
// (C) Copyright 2020 by Autodesk, Inc.
2 3 4 5 6 7

#ifndef BASE_ICHECKSUM_HH_INCLUDE
#define BASE_ICHECKSUM_HH_INCLUDE

#ifndef TEST_ON

8
#define TEST(CHKSM, RCRD)
Martin Marinov's avatar
Martin Marinov committed
9
#define TEST_if(CNDT, CHKSM, RCRD)
10

11 12 13 14 15 16 17
#else

#include <Base/Test/TestResult.hh>
#include <Base/Utils/OStringStream.hh>
#include <map>
#include <sstream>

18 19 20 21
namespace Test
{
namespace Checksum
{
22

23 24 25 26 27 28 29 30
//! Enumerate the checksum levels
enum Level
{
  L_NONE,
  L_STABLE,
  L_PRIME,
  L_ALL
};
31
extern Level run_lvl; //<! The checksum run level
32
const char* const LEVEL_TEXT[4] = {"NONE", "STABLE", "PRIME", "ALL"};
33

34 35 36 37 38 39 40 41 42 43 44 45 46 47
//! typedef String, this is used a lot in this namespace
typedef std::string String;

//! The checksum record
struct Record
{
  Record() {}
  Record(const Result& _rslt, const String _data) : rslt(_rslt), data(_data) {}

  Result rslt; //! record result
  String data; //!< recorded "data" (as string, can be parsed)
};

//! The difference found by the IChecksum::compare() operation
48
class Difference
49 50 51 52
{
public:
  enum Type
  {
53 54 55
    EQUAL,      // result is bitwise identical
    UNKNOWN,    // non-negligible difference, but of unknown quality
    IMPROVED,   // result is better
56 57
    NEGLEGIBLE, // result is negligibly different
    SUSPICIOUS, // result is different, and the new result might be worse
58 59 60
    REGRESSED,  // result is worse
    WORKED,     // result works now, but used to fail
    FAILED      // result fails now, but used to work
61 62 63 64
  };

  static const char* const type_text(const Type _type)
  {
65 66
    static const char dscr[][32] = {"EQUAL", "UNKNOWN", "IMPROVED",
        "NEGLEGIBLE", "SUSPICIOUS", "REGRESSED", "WORKED", "FAILED"};
67 68 69 70
    return dscr[_type];
  }

  Difference(const Type _type = EQUAL, const String& _dscr = String())
71 72 73
      : type_(_type), dscr_(_dscr)
  {
  }
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

  const Type type() const { return type_; }
  bool equal() const { return type() == EQUAL; }

  bool operator==(const Difference& _othr) const
  {
    return type_ == _othr.type_ && dscr_ == _othr.dscr_;
  }

  Difference& operator+=(const Difference& _othr)
  {
    if (type_ < _othr.type_)
      type_ = _othr.type_;
    if (dscr_.empty())
      dscr_ = _othr.dscr_;
    else if (!_othr.dscr_.empty())
      dscr_ += "; " + _othr.dscr_;
    return *this;
  }

  Difference& operator+=(const Difference::Type& _type)
  {
    if (type_ < _type)
      type_ = type_;
    return *this;
  }

101
  const String& description() const { return dscr_; }
102 103 104

  const char* const type_text() const { return type_text(type_); }

105 106
  friend Base::IOutputStream& operator<<(
      Base::IOutputStream& _os, Difference& _diff)
107 108 109 110 111
  {
    // TODO: use string description array
    return _os << _diff.type_text() << " " << _diff.dscr_;
  }

112 113
  bool operator<(const Difference& _othr) const { return type_ < _othr.type_; }

114 115 116 117 118 119 120 121 122 123 124 125 126
private:
  Type type_;
  String dscr_;
};

/*!
Base class for test checksums. Whatever check we want to add in the test system,
it must be an instance of a class derived from Checksum. All derived classes
must be instantiated as global variables.
*/
class Object
{
public:
127
  //! Checksum name.
128 129 130
  const char* const name() const { return name_; }

  //! Add a record the checksum (generic version)
131
  template <typename T> void record(const Result& _rslt, const T& _data)
132 133 134 135 136 137 138 139 140 141 142 143
  {
    Base::OStringStream strm;
    strm << _data;
    add(_rslt, strm.str);
  }

  //! Add a record of the checksum (public version)
  void record(const Result& _rslt, const String& _data) { add(_rslt, _data); }

  /*!
  Compare two existing records (old and new).
  Returns a qualification and a description of the difference.
144 145
  The default implementation has some intelligence in comparing the record
  results, but compares the the data simply as strings (no parsing).
146 147
  */
  virtual Difference compare(
148 149 150 151 152
      const Path& _old_path,   //!<[in] Path to the left record
      const Record& _old_rcrd, //!<[in] "Left" record
      const Path& _new_path,   //!<[in] Path to the right record
      const Record& _new_rcrd  //!<[in] "Right" record
  ) const;
153

154 155 156
  //! Get if the checksum should be run
  bool allow() const { return lvl_ <= run_lvl; }

157
protected:
158 159 160 161 162 163
  /*!
  Performs an automatic registration of the new checksum in a
  global list, and verifies that the name is unique.
  */
  Object(const char* const _name, const Level _lvl = L_ALL);

164 165 166 167 168 169
  //! Add a record of the checksum (protected version)
  void add(const Result& _rslt, const String& _data);

  //! Compare the data, the default implementation does a string comparison
  virtual Difference compare_data(const String& _old, const String& _new) const;

170 171 172
protected:
  const Level lvl_;

173 174 175 176 177 178 179 180 181
private:
  const char* const name_;

private:
  Object(const Object&);
  Object* operator=(const Object&);
};

/*!
182
Definition of the checksums registry. It is a map from a string (that is the
183 184
checksum name to an IChecksum.
*/
185
typedef std::map<String, Object*> Registry;
186 187 188 189 190 191

/*!
Function to get a static map with all the registered checksums.
*/
const Registry& registry();

192 193
} // namespace Checksum
} // namespace Test
194

195 196 197 198 199 200 201
#define TEST(CHKSM, RCRD) \
  { \
    if (Test::Checksum::CHKSM.allow()) \
    { \
      Test::Checksum::CHKSM.RCRD; \
    } \
  }
202

203 204 205 206 207
#define TEST_if(CNDT, CHKSM, RCRD) \
  { \
    if (CNDT) \
      TEST(CHKSM, RCRD) \
  }
208

209
#endif // TEST_ON
210

211
#endif // BASE_ICHECKSUM_HH_INCLUDE