Add some unit tests for Linux WriteSymbolFile

This patch adds synth_elf::{StringTable,SymbolTable,ELF} classes to
produce in-memory ELF files to properly test the Linux symbol dumping
code. It also uses those classes to add some basic tests for
the WriteSymbolFile function.

R=jimb at http://breakpad.appspot.com/277001/show

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@794 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
ted.mielczarek
2011-07-06 17:05:59 +00:00
parent b2f96f314c
commit 3ca4a120de
16 changed files with 958 additions and 228 deletions

View File

@@ -37,6 +37,7 @@
#include <string.h>
#include <algorithm>
#include <sstream>
#include <string>
#include "breakpad_googletest_includes.h"
@@ -44,53 +45,10 @@
using google_breakpad::Module;
using std::string;
using std::stringstream;
using std::vector;
using testing::ContainerEq;
// Return a FILE * referring to a temporary file that will be deleted
// automatically when the stream is closed or the program exits.
static FILE *checked_tmpfile() {
FILE *f = tmpfile();
if (!f) {
fprintf(stderr, "error creating temporary file: %s\n", strerror(errno));
exit(1);
}
return f;
}
// Read from STREAM until end of file, and return the contents as a
// string.
static string checked_read(FILE *stream) {
string contents;
int c;
while ((c = getc(stream)) != EOF)
contents.push_back(c);
if (ferror(stream)) {
fprintf(stderr, "error reading temporary file contents: %s\n",
strerror(errno));
exit(1);
}
return contents;
}
// Apply 'fflush' to STREAM, and check for errors.
static void checked_fflush(FILE *stream) {
if (fflush(stream) == EOF) {
fprintf(stderr, "error flushing temporary file stream: %s\n",
strerror(errno));
exit(1);
}
}
// Apply 'fclose' to STREAM, and check for errors.
static void checked_fclose(FILE *stream) {
if (fclose(stream) == EOF) {
fprintf(stderr, "error closing temporary file stream: %s\n",
strerror(errno));
exit(1);
}
}
static Module::Function *generate_duplicate_function(const string &name) {
const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cLL;
const Module::Address DUP_SIZE = 0x200b26e605f99071LL;
@@ -110,19 +68,16 @@ static Module::Function *generate_duplicate_function(const string &name) {
#define MODULE_ID "id-string"
TEST(Write, Header) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n",
contents.c_str());
}
TEST(Write, OneLineFunc) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
Module::File *file = m.FindFile("file_name.cc");
@@ -136,11 +91,8 @@ TEST(Write, OneLineFunc) {
function->lines.push_back(line);
m.AddFunction(function);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 file_name.cc\n"
"FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a"
@@ -150,7 +102,7 @@ TEST(Write, OneLineFunc) {
}
TEST(Write, RelativeLoadAddress) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Some source files. We will expect to see them in lexicographic order.
@@ -189,11 +141,8 @@ TEST(Write, RelativeLoadAddress) {
// the module must work fine.
m.SetLoadAddress(0x2ab698b0b6407073LL);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 filename-a.cc\n"
"FILE 1 filename-b.cc\n"
@@ -247,12 +196,9 @@ TEST(Write, OmitUnusedFiles) {
EXPECT_STREQ("filename3", vec[2]->name.c_str());
EXPECT_NE(-1, vec[2]->source_id);
FILE *f = checked_tmpfile();
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
stringstream s;
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 filename1\n"
"FILE 1 filename3\n"
@@ -264,7 +210,7 @@ TEST(Write, OmitUnusedFiles) {
}
TEST(Construct, AddFunctions) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Two functions.
@@ -287,11 +233,8 @@ TEST(Construct, AddFunctions) {
m.AddFunctions(vec.begin(), vec.end());
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988"
" _and_void\n"
@@ -308,7 +251,7 @@ TEST(Construct, AddFunctions) {
}
TEST(Construct, AddFrames) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// First STACK CFI entry, with no initial rules or deltas.
@@ -342,11 +285,8 @@ TEST(Construct, AddFrames) {
m.AddStackFrameEntry(entry3);
// Check that Write writes STACK CFI records properly.
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
"STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
@@ -411,7 +351,7 @@ TEST(Construct, UniqueFiles) {
}
TEST(Construct, DuplicateFunctions) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Two functions.
@@ -421,11 +361,8 @@ TEST(Construct, DuplicateFunctions) {
m.AddFunction(function1);
m.AddFunction(function2);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
" _without_form\n",
@@ -433,7 +370,7 @@ TEST(Construct, DuplicateFunctions) {
}
TEST(Construct, FunctionsWithSameAddress) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Two functions.
@@ -443,11 +380,8 @@ TEST(Construct, FunctionsWithSameAddress) {
m.AddFunction(function1);
m.AddFunction(function2);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
" _and_void\n"
@@ -459,7 +393,7 @@ TEST(Construct, FunctionsWithSameAddress) {
// Externs should be written out as PUBLIC records, sorted by
// address.
TEST(Construct, Externs) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Two externs.
@@ -473,11 +407,8 @@ TEST(Construct, Externs) {
m.AddExtern(extern1);
m.AddExtern(extern2);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
MODULE_ID " " MODULE_NAME "\n"
@@ -489,7 +420,7 @@ TEST(Construct, Externs) {
// Externs with the same address should only keep the first entry
// added.
TEST(Construct, DuplicateExterns) {
FILE *f = checked_tmpfile();
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Two externs.
@@ -503,11 +434,8 @@ TEST(Construct, DuplicateExterns) {
m.AddExtern(extern1);
m.AddExtern(extern2);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
m.Write(s);
string contents = s.str();
EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
MODULE_ID " " MODULE_NAME "\n"