blob: bb6129a971cdc0b1282c5ca803ee7ff6eb7b020a [file] [log] [blame]
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BLOATY_TESTS_TEST_H_
#define BLOATY_TESTS_TEST_H_
#include "bloaty.h"
#include <fstream>
#include <memory>
#include <string>
#include <unordered_set>
#include <tuple>
#include <vector>
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "strarr.h"
bool GetFileSize(const std::string& filename, uint64_t* size) {
FILE* file = fopen(filename.c_str(), "rb");
if (!file) {
std::cerr << "Couldn't get file size for: " << filename << "\n";
return false;
}
fseek(file, 0L, SEEK_END);
*size = ftell(file);
fclose(file);
return true;
}
std::string GetTestDirectory() {
char pathbuf[PATH_MAX];
if (!getcwd(pathbuf, sizeof(pathbuf))) {
return "";
}
std::string path(pathbuf);
size_t pos = path.rfind('/');
return path.substr(pos + 1);
}
#define NONE_STRING "[None]"
// Testing Bloaty requires a delicate balance. Bloaty's output is by its
// nature very compiler and platform dependent. So we want to verify correct
// operation without overspecifying how the platform should behave.
class BloatyTest : public ::testing::Test {
protected:
void CheckConsistencyForRow(const bloaty::RollupRow& row, bool is_toplevel) {
// If any children exist, they should sum up to this row's values.
// Also none of the children should have the same name.
std::unordered_set<std::string> names;
if (row.sorted_children.size() > 0) {
uint64_t vmtotal = 0;
uint64_t filetotal = 0;
for (const auto& child : row.sorted_children) {
vmtotal += child.vmsize;
filetotal += child.filesize;
CheckConsistencyForRow(child, false);
ASSERT_TRUE(names.insert(child.name).second);
ASSERT_FALSE(child.vmsize == 0 && child.filesize == 0);
}
if (!row.diff_mode) {
ASSERT_EQ(vmtotal, row.vmsize);
ASSERT_EQ(filetotal, row.filesize);
}
}
if (!is_toplevel && row.sorted_children.size() == 1) {
ASSERT_NE(NONE_STRING, row.sorted_children[0].name);
}
}
void CheckConsistency() {
CheckConsistencyForRow(*top_row_, true);
ASSERT_EQ("TOTAL", top_row_->name);
}
std::string JoinStrings(const std::vector<std::string>& strings) {
std::string ret = strings[0];
for (size_t i = 1; i < strings.size(); i++) {
ret += " " + strings[i];
}
return ret;
}
bool TryRunBloaty(const std::vector<std::string>& strings) {
std::cerr << "Running bloaty: " << JoinStrings(strings) << "\n";
output_.reset(new bloaty::RollupOutput);
top_row_ = &output_->toplevel_row();
bloaty::MmapInputFileFactory factory;
if (bloaty::BloatyMain(strings.size(), StrArr(strings).get(), factory,
output_.get())) {
CheckConsistency();
output_->PrettyPrint(&std::cerr);
return true;
} else {
std::cerr << "Bloaty returned error." << "\n";
return false;
}
}
void RunBloaty(const std::vector<std::string>& strings) {
ASSERT_TRUE(TryRunBloaty(strings));
}
void AssertBloatyFails(const std::vector<std::string>& strings,
const std::string& /*msg_regex*/) {
// TODO(haberman): verify msg_regex by making all errors logged to a
// standard place.
ASSERT_FALSE(TryRunBloaty(strings));
}
// Special constants for asserting of children.
static constexpr int kUnknown = -1;
static constexpr int kSameAsVM = -2; // Only for file size.
void AssertChildren(
const bloaty::RollupRow& row,
const std::vector<std::tuple<std::string, int, int>>& children) {
size_t i = 0;
for (const auto& child : row.sorted_children) {
std::string expected_name;
int expected_vm, expected_file;
std::tie(expected_name, expected_vm, expected_file) = children[i];
// Excluding leading '_' is kind of a hack to exclude symbols
// automatically inserted by the compiler, like __x86.get_pc_thunk.bx
// for 32-bit x86 builds or _IO_stdin_used in binaries.
//
// Excluding leading '[' is for things like this:
//
// [None]
// [ELF Headers]
// [AR Headers]
// etc.
if (child.name[0] == '[' || child.name[0] == '_') {
continue;
}
EXPECT_EQ(expected_name, child.name);
// <0 indicates that we don't know what the exact size should be (for
// example for functions).
if (expected_vm == kUnknown) {
// Always pass.
} else if (expected_vm > 0) {
EXPECT_EQ(expected_vm, child.vmsize);
} else {
ASSERT_TRUE(false);
}
if (expected_file == kUnknown) {
// Always pass.
} else if (expected_file == kSameAsVM) {
EXPECT_EQ(child.vmsize, child.filesize);
} else {
EXPECT_EQ(expected_file, child.filesize);
}
if (++i == children.size()) {
// We allow the actual data to have excess elements.
break;
}
}
// All expected elements must be present.
ASSERT_EQ(i, children.size());
}
const bloaty::RollupRow* FindRow(const std::string& name) {
for (const auto& child : top_row_->sorted_children) {
if (child.name == name) {
return &child;
}
}
EXPECT_TRUE(false) << name;
return nullptr;
}
std::unique_ptr<bloaty::RollupOutput> output_;
const bloaty::RollupRow* top_row_;
};
constexpr int BloatyTest::kUnknown;
constexpr int BloatyTest::kSameAsVM;
#endif // BLOATY_TESTS_TEST_H_