GTEST in Gecko

Introduction

  • Google C++ Testing Framework (Google Test)
  • On a variety of platforms(Linux, Mac OSX, Cygwin, Windows CE and Symbian).
  • Based on the xUnit architecture
  • Support a rich set of assertions, user-defined assertions, death tests, fatal and non-fatal failures, value- and type-parameterized tests and XML test report generation

Why gtest is good?

  • Fast
  • Independent and repeatable
  • Well organized and reflect the structure of the tested code
  • Portable and reusable
  • When tests fail, they should provide as much information about the problem as possible
  • The testing framework should liberate test writers from housekeeping chores and let them focus on the test content.

Basic Concepts

  • Start by writing assertions
    • statements that check whether a condition is true
    • result can be success, nonfatal failure, or fatal failure
  • Tests use assertions to verify the tested code's behavior
  • A test case contains one or many tests.
    • Group your tests into test cases that reflect the structure of the tested code
  • A test program can contain multiple test cases.

Test Program

Test Case

Test

Assertion

Assertions

  • Google Test assertions are macros.
  • When an assertion fails, Google Test prints the assertion's source file and line number location, along with a failure message.
    • Can supply a custom failure message which will be appended to original message (simply stream the custom failure message into the macro using << operator)
  • EXPECT_* v.s. ASSERT_*

failure message

Example

ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

for (int i = 0; i < x.size(); ++i) {
EXPECT_EQ(x[i],y[i]) << "Vectors x and y differ at index " << i;
}

Basic Assertions

FATAL NON-FATAL
ASSERT_TRUE(condition); EXPECT_TRUE(condition);
ASSERT_FALSE(condition); EXPECT_FALSE(condition);

Binary Comparison

FATAL NON-FATAL
ASSERT_EQ(val1, val2); EXPECT_EQ(val1, val2); val1 == val2
ASSERT_NE(val1, val2); EXPECT_NE(val1, val2); val1 != val2
ASSERT_LT(val1, val2); EXPECT_LT(val1, val2); val1 < val2
ASSERT_LE(val1, val2); EXPECT_LE(val1, val2); val1 <= val2
ASSERT_GT(val1, val2); EXPECT_GT(val1, val2); val1 > val2
ASSERT_GE(val1, val2); EXPECT_GE(val1, val2); val1 >= val2

String Comparison

FATAL NON-FATAL
ASSERT_STREQ(str1, str2); EXPECT_STREQ(str1, str2);
ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2);
ASSERT_STRCASEEQ(str1, str2); EXPECT_STRCASEEQ(str1, str2);
ASSERT_STRCASENE(str1, str2); EXPECT_STRCASENE(str1, str2);

For more string comparison tricks (substring, prefix, suffix, and regular expression matching), see the Advanced Google Test Guide.

Simple Tests

  • Use the TEST() macro to define and name a test function which don't return a value.
  • In this function, alone with any valid C++ statements, use the assertions to check values
  • Result is determined by the assertions.

TEST(test_case_name, test_name) {
... test body ...
}
int Factorial(int n); // Returns the factorial of n
// Tests factorial of 0
TEST(FactorialTest, HandlesZeroInput) {
EXPECT_EQ(1, Factorial(0));
}

// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}

Simple Tests

Test Fixtures

  • If there are two or more tests that operate on similar data, you can use a test fixture.
    • Derive a class from "::testing::Test". Start its body with protected: or public: as we'll access fixture member from sub-classses.
    • Inside the class, declare any objects you need.
    • If necessary, write a default constructor or SetUp() to prepare the object for each test.
      • A destructor or TearDown() function to release any resource you allocate in SetUp().
    • If needed, define subroutines for your tests

Test Fixtures

  • Use TEST_F() instead of TEST()
  • Define a test fixture class before using it in a TEST_F(), or you'll get the compiler error "virtual outside class declaration"
  • Steps of TEST_F():
    • Create a fresh test fixture at runtime
    • Immediately initialize it via SetUp()
    • Run the test
    • Clean up by calling TearDown()
    • Delete the test fixture

Test Fixtures

template <typename E> // E is the element type.
class Queue {
 public:
  Queue();
  void Enqueue(const E& element);
  E* Dequeue(); // Returns NULL if the queue is empty.
  size_t size() const;
  ...
};

Test Fixtures

class QueueTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    q1_.Enqueue(1);
    q2_.Enqueue(2);
    q2_.Enqueue(3);
  }

  // virtual void TearDown() {}

  Queue<int> q0_;
  Queue<int> q1_;
  Queue<int> q2_;
};
TEST_F(QueueTest, IsEmptyInitially) {
  EXPECT_EQ(0, q0_.size());
}

TEST_F(QueueTest, DequeueWorks) {
  int* n = q0_.Dequeue();
  EXPECT_EQ(NULL, n);

  n = q1_.Dequeue();
  ASSERT_TRUE(n != NULL);
  EXPECT_EQ(1, *n);
  EXPECT_EQ(0, q1_.size());
  delete n;

  n = q2_.Dequeue();
  ASSERT_TRUE(n != NULL);
  EXPECT_EQ(2, *n);
  EXPECT_EQ(1, q2_.size());
  delete n;
}

GTest in Gecko

  • The Mozilla build process will build GTest on supported platform(not yet supported on mobile)
  • However xul-gtest will only be built when tests are required to save an expensive second linking process
    • obj-xxx/dist/bin/gtest/libxul.so
  • Running selected tests
    • ./mach gtest Moz2D.*

GTest in Gecko

Adding a test

  • Find a gtest directory appropriate for the module
  • If none exist, create a directory as '<submodule>/tests/gtest'
    • Create a moz.build file in this directory.
    • Set "UNIFIED_SOURCE" to contain all of the test file names.
    • Set "SOURCES" and "LOCAL_INCLUDES" to include needed file
  • Set "TEST_DIRS" of a appropriate moz.build to contain your gtest directory

GTEST in Gecko

By Alphan Chen

GTEST in Gecko

  • 1,118