/* Copyright (c) 2008-2010, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file is part of ThreadSanitizer, a dynamic data race detector. // Author: Evgeniy Stepanov. // This file contains tests for suppressions implementation. #include <gtest/gtest.h> #include "suppressions.h" #define VEC(arr) *(new vector<string>(arr, arr + sizeof(arr) / sizeof(*arr))) class BaseSuppressionsTest : public ::testing::Test { protected: bool IsSuppressed(string tool, string warning_type, const vector<string>& f_m, const vector<string>& f_d, const vector<string>& o) { string result; return supp_.StackTraceSuppressed( tool, warning_type, f_m, f_d, o, &result); } bool IsSuppressed(const vector<string>& f_m, const vector<string>& f_d, const vector<string>& o) { return IsSuppressed("test_tool", "test_warning_type", f_m, f_d, o); } Suppressions supp_; }; class SuppressionsTest : public BaseSuppressionsTest { protected: virtual void SetUp() { const string data = "{\n" " name\n" " test_tool,tool2:test_warning_type\n" " fun:function1\n" " obj:object1\n" " fun:function2\n" "}"; supp_.ReadFromString(data); } }; TEST_F(SuppressionsTest, Simple) { string m[] = {"aa", "bb", "cc"}; string d[] = {"aaa", "bbb", "ccc"}; string o[] = {"object1", "object2", "object3"}; ASSERT_FALSE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(SuppressionsTest, Simple2) { string m[] = {"function1", "bb", "function2"}; string d[] = {"aaa", "bbb", "ccc"}; string o[] = {"object2", "object1", "object3"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } // A long stack trace is ok. TEST_F(SuppressionsTest, LongTrace) { string m[] = {"function1", "bb", "function2", "zz"}; string d[] = {"aaa", "bbb", "ccc", "zzz"}; string o[] = {"object2", "object1", "object3", "o4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } // A stack trace template only matches at the top of the stack. TEST_F(SuppressionsTest, OnlyMatchesAtTheTop) { string m[] = {"zz", "function1", "bb", "function2"}; string d[] = {"zzz", "aaa", "bbb", "ccc"}; string o[] = {"o0", "object2", "object1", "object3"}; ASSERT_FALSE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } // A short stack trace is not. TEST_F(SuppressionsTest, ShortTrace) { string m[] = {"function1", "bb"}; string d[] = {"aaa", "bbb"}; string o[] = {"object2", "object1"}; ASSERT_FALSE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } class SuppressionsWithWildcardsTest : public BaseSuppressionsTest { protected: virtual void SetUp() { const string data = "{\n" " name\n" " test_tool,tool2:test_warning_type\n" " fun:fun*1\n" " obj:obj*t1\n" " ...\n" " fun:f?n*2\n" "}"; supp_.ReadFromString(data); } }; TEST_F(SuppressionsWithWildcardsTest, Wildcards1) { string m[] = {"function1", "bb", "function2"}; string d[] = {"aaa", "bbb", "ccc"}; string o[] = {"object2", "object1", "object3"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(SuppressionsWithWildcardsTest, Wildcards2) { string m[] = {"some_other_function1", "bb", "function2"}; string d[] = {"aaa", "bbb", "ccc"}; string o[] = {"object2", "object1", "object3"}; ASSERT_FALSE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(SuppressionsWithWildcardsTest, Wildcards3) { string m[] = {"fun1", "bb", "fanction2"}; string d[] = {"aaa", "bbb", "ccc"}; string o[] = {"object2", "objt1", "object3"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } // Tests "..." wildcard. TEST_F(SuppressionsWithWildcardsTest, VerticalWildcards1) { string m[] = {"fun1", "bb", "qq", "fanction2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } class MultipleStackTraceTest : public BaseSuppressionsTest { protected: virtual void SetUp() { const string data = "{\n" " name\n" " test_tool,tool2:test_warning_type\n" " {\n" " fun:fun*1\n" " }\n" " {\n" " fun:fun*2\n" " fun:fun*3\n" " }\n" " {\n" " ...\n" " fun:fun*4\n" " obj:obj*5\n" " }\n" "}"; supp_.ReadFromString(data); } }; TEST_F(MultipleStackTraceTest, Simple1) { string m[] = {"fun1", "bb", "qq", "fun2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object1", "object2", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(MultipleStackTraceTest, SecondTemplateMatches) { string m[] = {"fun2", "fun3", "qq", "fun2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object1", "object2", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(MultipleStackTraceTest, ThirdTemplateMatches) { string m[] = {"fun4", "bb", "qq", "fun2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object1", "object5", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(MultipleStackTraceTest, NothingMatches) { string m[] = {"_fun1", "bb", "qq", "fun2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object1", "object2", "object3", "object4"}; ASSERT_FALSE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(MultipleStackTraceTest, TwoTemplatesMatch) { string m[] = {"fun1", "bb", "fun4", "fun2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object1", "object2", "object3", "object5"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, StartsWithVerticalWildcard) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " ...\n" " fun:qq\n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"fun1", "bb", "qq", "function2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, StartsWithVerticalWildcard2) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " ...\n" " fun:fun1\n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"fun1", "bb", "qq", "function2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, EndsWithVerticalWildcard) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:fun1\n" " ...\n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"fun1", "bb", "qq", "function2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, EndsWithVerticalWildcard2) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:qq\n" " ...\n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"fun1", "bb", "qq", "function2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_FALSE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, Complex) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:qq\n" " ...\n" " obj:obj*3\n" " ...\n" " fun:function?\n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"fun1", "bb", "qq", "function2"}; string d[] = {"aaa", "bbb", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_FALSE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, DemangledNames) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:bb*w?\n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"fun1", "bb", "qq", "function2"}; string d[] = {"bbbxxwz", "aaa", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, TrailingWhitespace) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:bb*w? \n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"fun1", "bb", "qq", "function2"}; string d[] = {"bbbxxwz", "aaa", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, ObjectiveC) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:-[NSObject(NSKeyValueCoding) setValue:forKeyPath:]\n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"-[NSObject(NSKeyValueCoding) setValue:forKeyPath:]", "function2"}; string d[] = {"bbbxxwz", "aaa", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } TEST_F(BaseSuppressionsTest, ComparisonAndShiftOperators) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:operator<\n" " fun:operator>\n" " fun:operator<=\n" " fun:operator>=\n" " fun:operator<<\n" " fun:operator>>\n" " fun:operator<<=\n" " fun:operator>>=\n" " fun:operator->\n" " fun:operator->*\n" "}"; ASSERT_GT(supp_.ReadFromString(data), 0); string m[] = {"operator<", "operator>", "operator<=", "operator>=", "operator<<", "operator>>", "operator<<=", "operator>>=", "operator->", "operator->*"}; string d[] = {"bbbxxwz", "aaa", "ddd", "ccc"}; string o[] = {"object2", "objt1", "object3", "object4"}; ASSERT_TRUE(IsSuppressed(VEC(m), VEC(d), VEC(o))); } class FailingSuppressionsTest : public ::testing::Test { protected: int ErrorLineNo(string data) { int result = supp_.ReadFromString(data); if (result >= 0) return -1; else return supp_.GetErrorLineNo(); } Suppressions supp_; }; TEST_F(FailingSuppressionsTest, NoOpeningBrace) { const string data = " name\n" " test_tool:test_warning_type\n" " fun:bb*w? \n" "}"; ASSERT_EQ(1, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, Bad1) { const string data = "{\n" " name\n" " something_else\n" " test_tool:test_warning_type\n" " fun:bb*w? \n" "}"; ASSERT_EQ(3, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, Bad2) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " extra\n" " fun:bb*w? \n" "}"; ASSERT_EQ(4, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, Bad3) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:bb*w? \n" " extra\n" "}"; ASSERT_EQ(5, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, SomeWeirdTextAfterASuppression) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " fun:bb*w? \n" "}\n" "some_weird_text\n" "after_a_suppression\n"; ASSERT_EQ(6, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, NoToolsLineInMultitraceSuppression) { const string data = "{\n" " name\n" " {\n" " fun:fun*2\n" " fun:fun*3\n" " }\n" " {\n" " ...\n" " fun:fun*4\n" " obj:obj*5\n" " }\n" "}"; ASSERT_EQ(3, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, BadStacktrace1) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " {\n" " fun:fun*2\n" " fun:fun*3\n" " }\n" " {\n" " zzz\n" " fun:fun*4\n" " obj:obj*5\n" " }\n" "}"; ASSERT_EQ(9, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, BadStacktrace2) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " {\n" " fun:fun*2\n" " fun:fun*3\n" " }\n" " {\n" " {\n" " fun:fun*4\n" " obj:obj*5\n" " }\n" "}"; ASSERT_EQ(9, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, BadStacktrace3) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " {\n" " fun:fun*2\n" " fun:fun*3\n" " }\n" " {\n" " fun:fun*4\n" " obj:obj*5\n" " }\n" " zzz\n" "}"; ASSERT_EQ(12, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, StacktraceWithParenthesis) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " {\n" " fun:fun*2\n" " fun:fun*3\n" " }\n" " {\n" " fun:fun*4()\n" " obj:obj*5\n" " }\n" "}"; ASSERT_EQ(9, ErrorLineNo(data)); } TEST_F(FailingSuppressionsTest, StacktraceWithAngleBraces) { const string data = "{\n" " name\n" " test_tool:test_warning_type\n" " {\n" " fun:fun*2\n" " fun:fun*3\n" " }\n" " {\n" " fun:fun<int>*4\n" " obj:obj*5\n" " }\n" "}"; ASSERT_EQ(9, ErrorLineNo(data)); } TEST(WildcardTest, Simple) { EXPECT_TRUE(StringMatch("abc", "abc")); EXPECT_FALSE(StringMatch("abcd", "abc")); EXPECT_FALSE(StringMatch("dabc", "abc")); EXPECT_FALSE(StringMatch("ab", "abc")); EXPECT_FALSE(StringMatch("", "abc")); EXPECT_FALSE(StringMatch("abc", "")); EXPECT_TRUE(StringMatch("", "")); } TEST(WildcardTest, SingleCharacterWildcard) { EXPECT_TRUE(StringMatch("a?c", "abc")); EXPECT_TRUE(StringMatch("?bc", "abc")); EXPECT_TRUE(StringMatch("ab?", "abc")); EXPECT_TRUE(StringMatch("a??", "abc")); EXPECT_TRUE(StringMatch("???", "abc")); EXPECT_TRUE(StringMatch("?", "a")); EXPECT_FALSE(StringMatch("?zc", "abc")); EXPECT_FALSE(StringMatch("?bz", "abc")); EXPECT_FALSE(StringMatch("b?c", "abc")); EXPECT_FALSE(StringMatch("az?", "abc")); EXPECT_FALSE(StringMatch("abc?", "abc")); EXPECT_FALSE(StringMatch("?abc", "abc")); EXPECT_FALSE(StringMatch("?", "")); EXPECT_FALSE(StringMatch("??", "")); } TEST(WildcardTest, MultiCharacterWildcard) { EXPECT_TRUE(StringMatch("*x", "x")); EXPECT_TRUE(StringMatch("x*", "x")); EXPECT_TRUE(StringMatch("*x*", "x")); EXPECT_TRUE(StringMatch("a*d", "abcd")); EXPECT_TRUE(StringMatch("ab*d", "abcd")); EXPECT_TRUE(StringMatch("*cd", "abcd")); EXPECT_TRUE(StringMatch("*d", "abcd")); EXPECT_TRUE(StringMatch("ab*", "abcd")); EXPECT_TRUE(StringMatch("a*", "abcd")); EXPECT_TRUE(StringMatch("*", "abcd")); EXPECT_TRUE(StringMatch("ab*cd", "abcd")); EXPECT_TRUE(StringMatch("ab**", "abcd")); EXPECT_TRUE(StringMatch("**", "abcd")); EXPECT_TRUE(StringMatch("***", "abcd")); EXPECT_TRUE(StringMatch("**d", "abcd")); EXPECT_TRUE(StringMatch("*c*", "abcd")); EXPECT_TRUE(StringMatch("a*c*d*f", "abcdef")); EXPECT_TRUE(StringMatch("a*c*e*", "abcdef")); EXPECT_TRUE(StringMatch("*a*b*f", "abcdef")); EXPECT_TRUE(StringMatch("*b*d*", "abcdef")); EXPECT_FALSE(StringMatch("b*", "abcd")); EXPECT_FALSE(StringMatch("*c", "abcd")); EXPECT_FALSE(StringMatch("*a", "abcd")); } TEST(WildcardTest, WildcardCharactersInText) { EXPECT_TRUE(StringMatch("?", "?")); EXPECT_FALSE(StringMatch("a", "?")); EXPECT_FALSE(StringMatch("ab", "a?")); EXPECT_FALSE(StringMatch("ab", "?b")); EXPECT_TRUE(StringMatch("a?", "a?")); EXPECT_TRUE(StringMatch("?b", "?b")); EXPECT_TRUE(StringMatch("*", "*")); EXPECT_FALSE(StringMatch("a", "*")); EXPECT_FALSE(StringMatch("ab", "a*")); EXPECT_FALSE(StringMatch("ab", "*b")); EXPECT_TRUE(StringMatch("a*", "a*")); EXPECT_TRUE(StringMatch("*b", "*b")); } int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }