| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #ifndef RUNTIME_PLATFORM_ASSERT_H_ | |
| #define RUNTIME_PLATFORM_ASSERT_H_ | |
| #include "platform/globals.h" | |
| #if !defined(DEBUG) && !defined(NDEBUG) | |
| #error neither DEBUG nor NDEBUG defined | |
| #elif defined(DEBUG) && defined(NDEBUG) | |
| #error both DEBUG and NDEBUG defined | |
| #endif | |
| // TODO(5411406): include sstream for now, once we have a Utils::toString() | |
| // implemented for all the primitive types we can replace the usage of | |
| // sstream by Utils::toString() | |
| #if defined(DEBUG) || defined(TESTING) | |
| #include <string.h> | |
| #include <sstream> | |
| #include <string> | |
| #endif | |
| namespace dart { | |
| class DynamicAssertionHelper { | |
| public: | |
| DynamicAssertionHelper(const char* file, int line) | |
| : file_(file), line_(line) {} | |
| protected: | |
| void Print(const char* format, | |
| va_list arguments, | |
| bool will_abort = false) const; | |
| const char* const file_; | |
| const int line_; | |
| DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicAssertionHelper); | |
| }; | |
| class Assert : public DynamicAssertionHelper { | |
| public: | |
| Assert(const char* file, int line) : DynamicAssertionHelper(file, line) {} | |
| DART_NORETURN void Fail(const char* format, ...) const PRINTF_ATTRIBUTE(2, 3); | |
| template <typename T> | |
| T NotNull(const T p); | |
| }; | |
| class Expect : public DynamicAssertionHelper { | |
| public: | |
| Expect(const char* file, int line) : DynamicAssertionHelper(file, line) {} | |
| void Fail(const char* format, ...) const PRINTF_ATTRIBUTE(2, 3); | |
| #if defined(TESTING) | |
| template <typename E, typename A> | |
| void Equals(const E& expected, const A& actual); | |
| template <typename E, typename A> | |
| void NotEquals(const E& not_expected, const A& actual); | |
| template <typename E, typename A> | |
| void BitEquals(const E& expected, const A& actual); | |
| template <typename E, typename A, typename T> | |
| void FloatEquals(const E& expected, const A& actual, const T& tol); | |
| void StringEquals(const char* expected, const char* actual); | |
| void StringEqualsNoPrefixSuffix(const char* expected, const char* actual); | |
| void IsSubstring(const char* needle, const char* haystack); | |
| void IsNotSubstring(const char* needle, const char* haystack); | |
| template <typename E, typename A> | |
| void LessThan(const E& left, const A& right); | |
| template <typename E, typename A> | |
| void LessEqual(const E& left, const A& right); | |
| template <typename E, typename A> | |
| void GreaterThan(const E& left, const A& right); | |
| template <typename E, typename A> | |
| void GreaterEqual(const E& left, const A& right); | |
| template <typename T> | |
| void NotNull(const T p); | |
| template <typename T> | |
| void Null(const T p); | |
| #endif | |
| static bool failed() { return failed_; } | |
| private: | |
| static bool failed_; | |
| }; | |
| template <typename T> | |
| T Assert::NotNull(const T p) { | |
| if (p != nullptr) return p; | |
| Fail("expected: not nullptr, found nullptr"); | |
| return nullptr; | |
| } | |
| #if defined(TESTING) | |
| // Only allow the expensive (with respect to code size) assertions | |
| // in testing code. | |
| template <typename E, typename A> | |
| void Expect::Equals(const E& expected, const A& actual) { | |
| if (actual == expected) return; | |
| std::ostringstream ess, ass; | |
| ess << expected; | |
| ass << actual; | |
| std::string es = ess.str(), as = ass.str(); | |
| Fail("expected: <%s> but was: <%s>", es.c_str(), as.c_str()); | |
| } | |
| template <typename E, typename A> | |
| void Expect::NotEquals(const E& not_expected, const A& actual) { | |
| if (actual != not_expected) return; | |
| std::ostringstream ness; | |
| ness << not_expected; | |
| std::string nes = ness.str(); | |
| Fail("did not expect: <%s>", nes.c_str()); | |
| } | |
| template <typename E, typename A> | |
| void Expect::BitEquals(const E& expected, const A& actual) { | |
| static_assert(sizeof(E) == sizeof(A)); | |
| if (memcmp(&expected, &actual, sizeof(E)) == 0) return; | |
| std::ostringstream ess, ass; | |
| ess << expected; | |
| ass << actual; | |
| std::string es = ess.str(), as = ass.str(); | |
| Fail("expected: <%s> but was: <%s>", es.c_str(), as.c_str()); | |
| } | |
| template <typename E, typename A, typename T> | |
| void Expect::FloatEquals(const E& expected, const A& actual, const T& tol) { | |
| if (((expected - tol) <= actual) && (actual <= (expected + tol))) { | |
| return; | |
| } | |
| std::ostringstream ess, ass, tolss; | |
| ess << expected; | |
| ass << actual; | |
| tolss << tol; | |
| std::string es = ess.str(), as = ass.str(), tols = tolss.str(); | |
| Fail("expected: <%s> but was: <%s> (tolerance: <%s>)", es.c_str(), as.c_str(), | |
| tols.c_str()); | |
| } | |
| static void Escape(std::string& dst, const char* src, int len) { | |
| if (len == 0) return; | |
| const char* const end = src + len; | |
| for (char c = *src; src < end; src++, c = *src) { | |
| if (c == '\n') { | |
| dst += "\\n\"\n\""; | |
| } else if (c == '\'') { | |
| dst += "\\\'"; | |
| } else if (c == '\"') { | |
| dst += "\\\""; | |
| } else if (c == '\\') { | |
| dst += "\\\\"; | |
| } else if (c == '\0') { | |
| dst += "\\0"; | |
| } else { | |
| dst += c; | |
| } | |
| } | |
| } | |
| // Splits two null-terminated strings into a common prefix and suffix | |
| // and the mismatched parts of the two strings, escaping the outputs. | |
| // | |
| // If the two strings are the same, then an escaped version of | |
| // the string is in the prefix and the other outputs are empty. | |
| // | |
| // If there is no common prefix and suffix or the size of the common | |
| // prefix and suffix combined is small in comparison to the size | |
| // of the two strings, then the prefix and suffix are empty to limit | |
| // the noise in the output of Expect::StringEquals. | |
| static void FindCommonPrefixAndSuffix(const char* expected, | |
| const char* actual, | |
| std::string& prefix, | |
| std::string& expected_mismatch, | |
| std::string& actual_mismatch, | |
| std::string& suffix) { | |
| const int expected_len = strlen(expected); | |
| const int actual_len = strlen(actual); | |
| int prefix_length = 0; | |
| while (expected[prefix_length] == actual[prefix_length]) { | |
| // Either the prefix or actual string is a prefix of the other. | |
| if (expected[prefix_length] == '\0' || actual[prefix_length] == '\0') { | |
| break; | |
| } | |
| prefix_length += 1; | |
| } | |
| if (prefix_length == expected_len && prefix_length == actual_len) { | |
| // The two are equal, so just escape the whole string into the prefix. | |
| Escape(prefix, expected, prefix_length); | |
| return; | |
| } | |
| int suffix_length = 0; | |
| while (expected[(expected_len - 1) - suffix_length] == | |
| actual[(actual_len - 1) - suffix_length]) { | |
| if (prefix_length + suffix_length == expected_len || | |
| prefix_length + suffix_length == actual_len) { | |
| // The prefix and suffix cover the entirety of one of the | |
| // two strings, meaning the actual string either has missing | |
| // or extra contents compared to the expected string. | |
| break; | |
| } | |
| suffix_length += 1; | |
| } | |
| // Set a minimum difference required to output a non-empty prefix and suffix, | |
| // so that they are not output if the common prefix and suffix of the expected | |
| // and actual strings is small. | |
| constexpr int kMinDifference = 10; | |
| if (prefix_length + suffix_length < kMinDifference) { | |
| prefix_length = 0; | |
| suffix_length = 0; | |
| } | |
| Escape(prefix, expected, prefix_length); | |
| Escape(expected_mismatch, expected + prefix_length, | |
| expected_len - prefix_length - suffix_length); | |
| Escape(actual_mismatch, actual + prefix_length, | |
| actual_len - prefix_length - suffix_length); | |
| Escape(suffix, expected + expected_len - suffix_length, suffix_length); | |
| } | |
| inline void Expect::StringEquals(const char* expected, const char* actual) { | |
| if (actual == nullptr) { | |
| Fail("expected:\n<\"%s\">\nbut was nullptr", expected); | |
| } else { | |
| if (strcmp(expected, actual) == 0) return; | |
| std::string prefix, expected_mismatch, actual_mismatch, suffix; | |
| FindCommonPrefixAndSuffix(expected, actual, prefix, expected_mismatch, | |
| actual_mismatch, suffix); | |
| // Only print the prefix and/or suffix if non-empty. | |
| if (prefix.empty() && suffix.empty()) { | |
| Fail("expected:\n<\"%s\">\nbut was:\n<\"%s\">", expected_mismatch.c_str(), | |
| actual_mismatch.c_str()); | |
| } else if (prefix.empty()) { | |
| Fail( | |
| "substring mismatch:\nexpected:\n <\"%s\">\nbut was:\n " | |
| "<\"%s\">\nsuffix:\n <\"%s\">", | |
| expected_mismatch.c_str(), actual_mismatch.c_str(), suffix.c_str()); | |
| } else if (suffix.empty()) { | |
| Fail("prefix:\n <\"%s\">\nexpected:\n <\"%s\">\nbut was:\n <\"%s\">", | |
| prefix.c_str(), expected_mismatch.c_str(), actual_mismatch.c_str()); | |
| } else { | |
| Fail( | |
| "prefix:\n <\"%s\">\nexpected:\n <\"%s\">\nbut was:\n " | |
| "<\"%s\">\nsuffix:\n <\"%s\">", | |
| prefix.c_str(), expected_mismatch.c_str(), actual_mismatch.c_str(), | |
| suffix.c_str()); | |
| } | |
| } | |
| } | |
| static void EscapeNoPrefixSuffix(std::string& dst, const char* src) { | |
| char c; | |
| while ((c = *src++) != '\0') { | |
| if (c == '\n') { | |
| dst += "\\n\"\n\""; | |
| } else if (c == '\'') { | |
| dst += "\\\'"; | |
| } else if (c == '\"') { | |
| dst += "\\\""; | |
| } else if (c == '\\') { | |
| dst += "\\\\"; | |
| } else { | |
| dst += c; | |
| } | |
| } | |
| } | |
| inline void Expect::StringEqualsNoPrefixSuffix(const char* expected, | |
| const char* actual) { | |
| if (strcmp(expected, actual) == 0) return; | |
| if (actual == nullptr) { | |
| Fail("expected:\n<\"%s\">\nbut was nullptr", expected); | |
| } else { | |
| if (strcmp(expected, actual) == 0) return; | |
| std::string es, as; | |
| EscapeNoPrefixSuffix(es, expected); | |
| EscapeNoPrefixSuffix(as, actual); | |
| Fail("expected:\n<\"%s\">\nbut was:\n<\"%s\">", es.c_str(), as.c_str()); | |
| } | |
| } | |
| inline void Expect::IsSubstring(const char* needle, const char* haystack) { | |
| if (strstr(haystack, needle) != nullptr) return; | |
| Fail("expected <\"%s\"> to be a substring of <\"%s\">", needle, haystack); | |
| } | |
| inline void Expect::IsNotSubstring(const char* needle, const char* haystack) { | |
| if (strstr(haystack, needle) == nullptr) return; | |
| Fail("expected <\"%s\"> to not be a substring of <\"%s\">", needle, haystack); | |
| } | |
| template <typename E, typename A> | |
| void Expect::LessThan(const E& left, const A& right) { | |
| if (left < right) return; | |
| std::ostringstream ess, ass; | |
| ess << left; | |
| ass << right; | |
| std::string es = ess.str(), as = ass.str(); | |
| Fail("expected: %s < %s", es.c_str(), as.c_str()); | |
| } | |
| template <typename E, typename A> | |
| void Expect::LessEqual(const E& left, const A& right) { | |
| if (left <= right) return; | |
| std::ostringstream ess, ass; | |
| ess << left; | |
| ass << right; | |
| std::string es = ess.str(), as = ass.str(); | |
| Fail("expected: %s <= %s", es.c_str(), as.c_str()); | |
| } | |
| template <typename E, typename A> | |
| void Expect::GreaterThan(const E& left, const A& right) { | |
| if (left > right) return; | |
| std::ostringstream ess, ass; | |
| ess << left; | |
| ass << right; | |
| std::string es = ess.str(), as = ass.str(); | |
| Fail("expected: %s > %s", es.c_str(), as.c_str()); | |
| } | |
| template <typename E, typename A> | |
| void Expect::GreaterEqual(const E& left, const A& right) { | |
| if (left >= right) return; | |
| std::ostringstream ess, ass; | |
| ess << left; | |
| ass << right; | |
| std::string es = ess.str(), as = ass.str(); | |
| Fail("expected: %s >= %s", es.c_str(), as.c_str()); | |
| } | |
| template <typename T> | |
| void Expect::NotNull(const T p) { | |
| if (p != nullptr) return; | |
| Fail("expected: not nullptr, found nullptr"); | |
| } | |
| template <typename T> | |
| void Expect::Null(const T p) { | |
| if (p == nullptr) return; | |
| Fail("expected: nullptr, found not null pointer"); | |
| } | |
| #endif | |
| } // namespace dart | |
| #define FATAL(format, ...) \ | |
| dart::Assert(__FILE__, __LINE__).Fail(format, ##__VA_ARGS__); | |
| #define UNIMPLEMENTED() FATAL("unimplemented code") | |
| #define UNREACHABLE() FATAL("unreachable code") | |
| #define OUT_OF_MEMORY() FATAL("Out of memory.") | |
| #if defined(DEBUG) | |
| // DEBUG binaries use assertions in the code. | |
| // Note: We wrap the if statement in a do-while so that we get a compile | |
| // error if there is no semicolon after ASSERT(condition). This | |
| // ensures that we get the same behavior on DEBUG and RELEASE builds. | |
| #define ASSERT(cond) \ | |
| do { \ | |
| if (!(cond)) dart::Assert(__FILE__, __LINE__).Fail("expected: %s", #cond); \ | |
| } while (false) | |
| #define ASSERT_EQUAL(actual, expected) \ | |
| do { \ | |
| if ((expected) != (actual)) { \ | |
| const std::string actual_str = std::to_string(actual); \ | |
| const std::string expected_str = std::to_string(expected); \ | |
| dart::Assert(__FILE__, __LINE__) \ | |
| .Fail("expected \"%s\" = %s, actual \"%s\" = %s", #expected, \ | |
| expected_str.c_str(), #actual, actual_str.c_str()); \ | |
| } \ | |
| } while (false) | |
| #define ASSERT_LESS_OR_EQUAL(actual, expected) \ | |
| do { \ | |
| if ((actual) > (expected)) { \ | |
| const std::string actual_str = std::to_string(actual); \ | |
| const std::string expected_str = std::to_string(expected); \ | |
| dart::Assert(__FILE__, __LINE__) \ | |
| .Fail("expected \"%s\" = %s >= actual \"%s\" = %s", #expected, \ | |
| expected_str.c_str(), #actual, actual_str.c_str()); \ | |
| } \ | |
| } while (false) | |
| #define ASSERT_IMPLIES(antecedent, consequent) \ | |
| do { \ | |
| if (antecedent) { \ | |
| ASSERT(consequent); \ | |
| } \ | |
| } while (false) | |
| // DEBUG_ASSERT allows identifiers in condition to be undeclared in release | |
| // mode. | |
| #define DEBUG_ASSERT(cond) ASSERT(cond) | |
| // Returns 'ptr'; useful for initializer lists: | |
| // class Foo { Foo(int* ptr) : ptr_(ASSERT_NOTNULL(ptr)) ... | |
| #define ASSERT_NOTNULL(ptr) dart::Assert(__FILE__, __LINE__).NotNull((ptr)) | |
| #else // if defined(DEBUG) | |
| // In order to avoid variable unused warnings for code that only uses | |
| // a variable in an ASSERT or EXPECT, we make sure to use the macro | |
| // argument. | |
| #define ASSERT(condition) \ | |
| do { \ | |
| } while (false && (condition)) | |
| #define ASSERT_EQUAL(expected, actual) \ | |
| do { \ | |
| } while (false && ((expected) != (actual))) | |
| #define ASSERT_LESS_OR_EQUAL(expected, actual) \ | |
| do { \ | |
| } while (false && ((actual) > (expected))) | |
| #define ASSERT_IMPLIES(antecedent, consequent) \ | |
| do { \ | |
| } while (false && (!(antecedent) || (consequent))) | |
| #define DEBUG_ASSERT(cond) | |
| #define ASSERT_NOTNULL(ptr) (ptr) | |
| #endif // if defined(DEBUG) | |
| #define RELEASE_ASSERT(cond) \ | |
| do { \ | |
| if (!(cond)) dart::Assert(__FILE__, __LINE__).Fail("expected: %s", #cond); \ | |
| } while (false) | |
| #define RELEASE_ASSERT_WITH_MSG(cond, msg) \ | |
| do { \ | |
| if (!(cond)) { \ | |
| dart::Assert(__FILE__, __LINE__).Fail("%s: expected: %s", msg, #cond); \ | |
| } \ | |
| } while (false) | |
| #define COMPILE_ASSERT(expr) static_assert(expr, "") | |
| #if defined(TESTING) | |
| // EXPECT and FAIL are equivalent to ASSERT and FATAL except that they do not | |
| // cause early termination of the unit test. This allows testing to proceed | |
| // further to be able to report other failures before reporting the overall | |
| // unit tests as failing. | |
| #define EXPECT(condition) \ | |
| if (!(condition)) { \ | |
| dart::Expect(__FILE__, __LINE__).Fail("expected: %s", #condition); \ | |
| } | |
| #define EXPECT_EQ(expected, actual) \ | |
| dart::Expect(__FILE__, __LINE__).Equals((expected), (actual)) | |
| #define EXPECT_NE(not_expected, actual) \ | |
| dart::Expect(__FILE__, __LINE__).NotEquals((not_expected), (actual)) | |
| #define EXPECT_BITEQ(expected, actual) \ | |
| dart::Expect(__FILE__, __LINE__).BitEquals((expected), (actual)) | |
| #define EXPECT_FLOAT_EQ(expected, actual, tol) \ | |
| dart::Expect(__FILE__, __LINE__).FloatEquals((expected), (actual), (tol)) | |
| #define EXPECT_STREQ(expected, actual) \ | |
| dart::Expect(__FILE__, __LINE__).StringEquals((expected), (actual)) | |
| #define EXPECT_STREQ_NO_PREFIX_SUFFIX(expected, actual) \ | |
| dart::Expect(__FILE__, __LINE__) \ | |
| .StringEqualsNoPrefixSuffix((expected), (actual)) | |
| #define EXPECT_SUBSTRING(needle, haystack) \ | |
| dart::Expect(__FILE__, __LINE__).IsSubstring((needle), (haystack)) | |
| #define EXPECT_NOTSUBSTRING(needle, haystack) \ | |
| dart::Expect(__FILE__, __LINE__).IsNotSubstring((needle), (haystack)) | |
| #define EXPECT_LT(left, right) \ | |
| dart::Expect(__FILE__, __LINE__).LessThan((left), (right)) | |
| #define EXPECT_LE(left, right) \ | |
| dart::Expect(__FILE__, __LINE__).LessEqual((left), (right)) | |
| #define EXPECT_GT(left, right) \ | |
| dart::Expect(__FILE__, __LINE__).GreaterThan((left), (right)) | |
| #define EXPECT_GE(left, right) \ | |
| dart::Expect(__FILE__, __LINE__).GreaterEqual((left), (right)) | |
| #define EXPECT_NOTNULL(ptr) dart::Expect(__FILE__, __LINE__).NotNull((ptr)) | |
| #define EXPECT_NULLPTR(ptr) dart::Expect(__FILE__, __LINE__).Null((ptr)) | |
| #define FAIL(format, ...) \ | |
| dart::Expect(__FILE__, __LINE__).Fail(format, ##__VA_ARGS__) | |
| #endif // defined(TESTING) | |
| #endif // RUNTIME_PLATFORM_ASSERT_H_ |