blob: 5b6b784be9919129d100c41481eb9f7c8a46a01f [file] [log] [blame]
// Copyright 2013 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.
#include "syzygy/pe/coff_relinker.h"
#include "base/file_util.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "syzygy/common/defs.h"
#include "syzygy/core/unittest_util.h"
#include "syzygy/pe/unittest_util.h"
namespace pe {
namespace {
using block_graph::BlockGraph;
using block_graph::BlockGraphOrdererInterface;
using block_graph::BlockGraphTransformInterface;
using block_graph::OrderedBlockGraph;
using block_graph::TransformPolicyInterface;
using testing::_;
using testing::Return;
using testing::StrictMock;
class TestCoffRelinker : public CoffRelinker {
public:
explicit TestCoffRelinker(const CoffTransformPolicy* transform_policy)
: CoffRelinker(transform_policy) {
}
using CoffRelinker::transforms_;
using CoffRelinker::orderers_;
};
class CoffRelinkerTest : public testing::PELibUnitTest {
public:
virtual void SetUp() OVERRIDE {
testing::PELibUnitTest::SetUp();
test_dll_obj_path_ =
testing::GetExeTestDataRelativePath(testing::kTestDllCoffObjName);
ASSERT_NO_FATAL_FAILURE(CreateTemporaryDir(&temp_dir_path_));
new_test_dll_obj_path_ = temp_dir_path_.Append(L"test_dll.obj");
new_test_dll_path_ = temp_dir_path_.Append(testing::kTestDllName);
}
CoffTransformPolicy policy_;
base::FilePath test_dll_obj_path_;
base::FilePath new_test_dll_obj_path_;
base::FilePath new_test_dll_path_;
base::FilePath temp_dir_path_;
};
class MockTransform : public BlockGraphTransformInterface {
public:
const char* name() const { return "MockTransform"; }
MOCK_METHOD3(TransformBlockGraph,
bool(const TransformPolicyInterface*,
BlockGraph*,
BlockGraph::Block*));
};
class MockOrderer : public BlockGraphOrdererInterface {
public:
const char* name() const { return "MockOrderer"; }
MOCK_METHOD2(OrderBlockGraph,
bool(OrderedBlockGraph*,
BlockGraph::Block*));
};
} // namespace
TEST_F(CoffRelinkerTest, InitFailsOnUnspecifiedInput) {
TestCoffRelinker relinker(&policy_);
relinker.set_output_path(new_test_dll_obj_path_);
EXPECT_FALSE(relinker.Init());
}
TEST_F(CoffRelinkerTest, InitFailsOnUnspecifiedOutput) {
TestCoffRelinker relinker(&policy_);
relinker.set_input_path(test_dll_obj_path_);
EXPECT_FALSE(relinker.Init());
}
TEST_F(CoffRelinkerTest, InitFailsOnNonexistentInput) {
TestCoffRelinker relinker(&policy_);
relinker.set_input_path(temp_dir_path_.Append(L"nonexistent.dll"));
relinker.set_output_path(new_test_dll_obj_path_);
EXPECT_FALSE(relinker.Init());
}
TEST_F(CoffRelinkerTest, InitFailsOnDisallowedOverwrite) {
TestCoffRelinker relinker(&policy_);
// Copy the image in case the test actually does overwrite the input; this
// way we don't accidentally turf our test data.
base::CopyFile(test_dll_obj_path_, new_test_dll_obj_path_);
relinker.set_input_path(new_test_dll_obj_path_);
relinker.set_output_path(new_test_dll_obj_path_);
relinker.set_allow_overwrite(false);
EXPECT_FALSE(relinker.Init());
}
TEST_F(CoffRelinkerTest, InitSucceeds) {
TestCoffRelinker relinker(&policy_);
relinker.set_input_path(test_dll_obj_path_);
relinker.set_output_path(new_test_dll_obj_path_);
EXPECT_TRUE(relinker.Init());
}
TEST_F(CoffRelinkerTest, IntermediateAccessors) {
TestCoffRelinker relinker(&policy_);
relinker.set_input_path(test_dll_obj_path_);
relinker.set_output_path(new_test_dll_obj_path_);
EXPECT_TRUE(relinker.Init());
EXPECT_EQ(test_dll_obj_path_, relinker.input_image_file().path());
EXPECT_TRUE(relinker.headers_block() != NULL);
}
TEST_F(CoffRelinkerTest, FailsWhenTransformFails) {
TestCoffRelinker relinker(&policy_);
StrictMock<MockTransform> transform;
EXPECT_CALL(transform, TransformBlockGraph(_, _, _)).WillOnce(Return(false));
relinker.AppendTransform(&transform);
relinker.set_input_path(test_dll_obj_path_);
relinker.set_output_path(new_test_dll_obj_path_);
EXPECT_TRUE(relinker.Init());
EXPECT_FALSE(relinker.Relink());
}
TEST_F(CoffRelinkerTest, FailsWhenOrdererFails) {
TestCoffRelinker relinker(&policy_);
StrictMock<MockOrderer> orderer;
EXPECT_CALL(orderer, OrderBlockGraph(_, _)).WillOnce(Return(false));
relinker.AppendOrderer(&orderer);
relinker.set_input_path(test_dll_obj_path_);
relinker.set_output_path(new_test_dll_obj_path_);
EXPECT_TRUE(relinker.Init());
EXPECT_FALSE(relinker.Relink());
}
TEST_F(CoffRelinkerTest, Success) {
TestCoffRelinker relinker(&policy_);
StrictMock<MockTransform> transform;
StrictMock<MockOrderer> orderer;
EXPECT_CALL(transform, TransformBlockGraph(_, _, _)).WillOnce(Return(true));
EXPECT_CALL(orderer, OrderBlockGraph(_, _)).WillOnce(Return(true));
relinker.AppendTransform(&transform);
relinker.AppendOrderer(&orderer);
relinker.set_input_path(test_dll_obj_path_);
relinker.set_output_path(new_test_dll_obj_path_);
EXPECT_TRUE(relinker.Init());
EXPECT_TRUE(relinker.Relink());
}
TEST_F(CoffRelinkerTest, IdentityRelink) {
TestCoffRelinker relinker(&policy_);
relinker.set_input_path(test_dll_obj_path_);
relinker.set_output_path(new_test_dll_obj_path_);
EXPECT_TRUE(relinker.Init());
EXPECT_TRUE(relinker.Relink());
EXPECT_TRUE(base::PathExists(relinker.output_path()));
// We assume the contents of the file are what they should be;
// CoffImageLayoutBuilder has more unit tests that check the specifics of
// writing COFF files.
}
} // namespace pe