/* 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();
}