diff options
Diffstat (limited to 'src/tests/profiledata_unittest.cc')
-rw-r--r-- | src/tests/profiledata_unittest.cc | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/src/tests/profiledata_unittest.cc b/src/tests/profiledata_unittest.cc new file mode 100644 index 0000000..a49257d --- /dev/null +++ b/src/tests/profiledata_unittest.cc @@ -0,0 +1,320 @@ +// Copyright (c) 2007, 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. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * 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. +// +// --- +// Author: Chris Demetriou +// +// This file contains the unit tests for the ProfileData class. + +#if defined HAVE_STDINT_H +#include <stdint.h> // to get uintptr_t +#elif defined HAVE_INTTYPES_H +#include <inttypes.h> // another place uintptr_t might be defined +#endif +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <string.h> +#include <string> + +#include "profiledata.h" + +#include "base/commandlineflags.h" +#include "base/logging.h" + +using std::string; + +// Some helpful macros for the test class +#define EXPECT_TRUE(cond) CHECK(cond) +#define EXPECT_FALSE(cond) CHECK(!(cond)) +#define EXPECT_EQ(a, b) CHECK_EQ(a, b) +#define EXPECT_GT(a, b) CHECK_GT(a, b) +#define EXPECT_LT(a, b) CHECK_LT(a, b) +#define EXPECT_GE(a, b) CHECK_GE(a, b) +#define EXPECT_LE(a, b) CHECK_LE(a, b) +#define EXPECT_STREQ(a, b) CHECK(strcmp(a, b) == 0) +#define TEST_F(cls, fn) void cls :: fn() + + +namespace { + +// must be the same as with ProfileData::Slot. +typedef uintptr_t ProfileDataSlot; + +// Quick and dirty function to make a number into a void* for use in a +// sample. +inline void* V(intptr_t x) { return reinterpret_cast<void*>(x); } + +class ProfileDataChecker { + public: + ProfileDataChecker() { + const char* tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + tmpdir = "/tmp"; + mkdir(tmpdir, 0755); // if necessary + filename_ = string(tmpdir) + "/profiledata_unittest.tmp"; + } + + string filename() const { return filename_; } + + void Check(const ProfileDataSlot* slots, int num_slots) { + size_t num_bytes = num_slots * sizeof slots[0]; + char* filedata = new char[num_bytes]; + + int fd = open(filename_.c_str(), O_RDONLY); + EXPECT_GE(fd, 0); + ssize_t numread = read(fd, filedata, num_bytes); + EXPECT_EQ(numread, num_bytes); + EXPECT_EQ(0, memcmp(slots, filedata, num_bytes)); + close(fd); + + delete[] filedata; + } + + private: + string filename_; +}; + +class ProfileDataTest { + protected: + void ExpectStopped() { + EXPECT_FALSE(collector_.enabled()); + } + + void ExpectRunningSamples(int samples) { + ProfileData::State state; + collector_.GetCurrentState(&state); + EXPECT_TRUE(state.enabled); + EXPECT_EQ(samples, state.samples_gathered); + } + + void ExpectSameState(const ProfileData::State& before, + const ProfileData::State& after) { + EXPECT_EQ(before.enabled, after.enabled); + EXPECT_EQ(before.samples_gathered, after.samples_gathered); + EXPECT_EQ(before.start_time, after.start_time); + EXPECT_STREQ(before.profile_name, after.profile_name); + } + + ProfileData collector_; + ProfileDataChecker checker_; + + private: + // The tests to run + void OpsWhenStopped(); + void StartStopEmpty(); + void StartWhenStarted(); + void StartStopEmpty2(); + void CollectOne(); + void CollectTwoMatching(); + void CollectTwoFlush(); + + public: +#define RUN(test) do { \ + printf("Running %s\n", #test); \ + ProfileDataTest pdt; \ + pdt.test(); \ +} while (0) + + static int RUN_ALL_TESTS() { + RUN(OpsWhenStopped); + RUN(StartStopEmpty); + RUN(StartWhenStarted); + RUN(StartStopEmpty2); + RUN(CollectOne); + RUN(CollectTwoMatching); + RUN(CollectTwoFlush); + return 0; + } +}; + +// Check that various operations are safe when stopped. +TEST_F(ProfileDataTest, OpsWhenStopped) { + ExpectStopped(); + EXPECT_FALSE(collector_.enabled()); + + // Verify that state is disabled, all-empty/all-0 + ProfileData::State state_before; + collector_.GetCurrentState(&state_before); + EXPECT_FALSE(state_before.enabled); + EXPECT_EQ(0, state_before.samples_gathered); + EXPECT_EQ(0, state_before.start_time); + EXPECT_STREQ("", state_before.profile_name); + + // Safe to call stop again. + collector_.Stop(); + + // Safe to call FlushTable. + collector_.FlushTable(); + + // Safe to call Add. + const void *trace[] = { V(100), V(101), V(102), V(103), V(104) }; + collector_.Add(arraysize(trace), trace); + + ProfileData::State state_after; + collector_.GetCurrentState(&state_after); + + ExpectSameState(state_before, state_after); +} + +// Start and Stop, collecting no samples. Verify output contents. +TEST_F(ProfileDataTest, StartStopEmpty) { + const int frequency = 1; + ProfileDataSlot slots[] = { + 0, 3, 0, 1000000 / frequency, 0, // binary header + 0, 1, 0 // binary trailer + }; + + ExpectStopped(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + ExpectRunningSamples(0); + collector_.Stop(); + ExpectStopped(); + checker_.Check(slots, arraysize(slots)); +} + +// Start after already started. Should return false and not impact +// collected data or state. +TEST_F(ProfileDataTest, StartWhenStarted) { + const int frequency = 1; + ProfileDataSlot slots[] = { + 0, 3, 0, 1000000 / frequency, 0, // binary header + 0, 1, 0 // binary trailer + }; + + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + + ProfileData::State state_before; + collector_.GetCurrentState(&state_before); + + CHECK(!collector_.Start("foobar", frequency * 2)); + + ProfileData::State state_after; + collector_.GetCurrentState(&state_after); + ExpectSameState(state_before, state_after); + + collector_.Stop(); + ExpectStopped(); + checker_.Check(slots, arraysize(slots)); +} + +// Like StartStopEmpty, but uses a different file name and frequency. +TEST_F(ProfileDataTest, StartStopEmpty2) { + const int frequency = 2; + ProfileDataSlot slots[] = { + 0, 3, 0, 1000000 / frequency, 0, // binary header + 0, 1, 0 // binary trailer + }; + + ExpectStopped(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + ExpectRunningSamples(0); + collector_.Stop(); + ExpectStopped(); + checker_.Check(slots, arraysize(slots)); +} + +TEST_F(ProfileDataTest, CollectOne) { + const int frequency = 2; + ProfileDataSlot slots[] = { + 0, 3, 0, 1000000 / frequency, 0, // binary header + 1, 5, 100, 101, 102, 103, 104, // our sample + 0, 1, 0 // binary trailer + }; + + ExpectStopped(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + ExpectRunningSamples(0); + + const void *trace[] = { V(100), V(101), V(102), V(103), V(104) }; + collector_.Add(arraysize(trace), trace); + ExpectRunningSamples(1); + + collector_.Stop(); + ExpectStopped(); + checker_.Check(slots, arraysize(slots)); +} + +TEST_F(ProfileDataTest, CollectTwoMatching) { + const int frequency = 2; + ProfileDataSlot slots[] = { + 0, 3, 0, 1000000 / frequency, 0, // binary header + 2, 5, 100, 201, 302, 403, 504, // our two samples + 0, 1, 0 // binary trailer + }; + + ExpectStopped(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + ExpectRunningSamples(0); + + for (int i = 0; i < 2; ++i) { + const void *trace[] = { V(100), V(201), V(302), V(403), V(504) }; + collector_.Add(arraysize(trace), trace); + ExpectRunningSamples(i + 1); + } + + collector_.Stop(); + ExpectStopped(); + checker_.Check(slots, arraysize(slots)); +} + +TEST_F(ProfileDataTest, CollectTwoFlush) { + const int frequency = 2; + ProfileDataSlot slots[] = { + 0, 3, 0, 1000000 / frequency, 0, // binary header + 1, 5, 100, 201, 302, 403, 504, // first sample (flushed) + 1, 5, 100, 201, 302, 403, 504, // second identical sample + 0, 1, 0 // binary trailer + }; + + ExpectStopped(); + EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), frequency)); + ExpectRunningSamples(0); + + const void *trace[] = { V(100), V(201), V(302), V(403), V(504) }; + + collector_.Add(arraysize(trace), trace); + ExpectRunningSamples(1); + collector_.FlushTable(); + + collector_.Add(arraysize(trace), trace); + ExpectRunningSamples(2); + + collector_.Stop(); + ExpectStopped(); + checker_.Check(slots, arraysize(slots)); +} + +} // namespace + +int main(int argc, char** argv) { + int rc = ProfileDataTest::RUN_ALL_TESTS(); + printf("%s\n", rc == 0 ? "PASS" : "FAIL"); + return rc; +} |