blob: 90b5664a5af6c05b645b8fc2cba2f9426e38aab4 [file] [log] [blame]
// Copyright 2012 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/application/application.h"
#include <shellapi.h>
#include "base/files/file_util.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "syzygy/common/unittest_util.h"
namespace application {
using ::testing::Return;
using ::testing::ScopedLogLevelSaver;
using ::testing::StrictMock;
using ::testing::_;
namespace {
const char kTestAppImplName[] = "Test Application (Unit Test)";
// A test application that simply makes the AppImplBase concrete.
class TestAppImpl : public AppImplBase {
public:
TestAppImpl() : AppImplBase(kTestAppImplName) {
}
};
// A mock application implementation class
class MockAppImpl : public AppImplBase {
public:
MockAppImpl() : AppImplBase("Mock Application (Unit Test)") {
}
MOCK_METHOD1(ParseCommandLine, bool(const base::CommandLine*));
MOCK_METHOD0(SetUp, bool());
MOCK_METHOD0(Run, int());
MOCK_METHOD0(TearDown, bool());
};
// Handy types we'll use below.
typedef Application<TestAppImpl, INIT_LOGGING_YES> TestApp;
typedef Application<StrictMock<MockAppImpl>, INIT_LOGGING_NO> MockApp;
// A basic test fixture that sets up dummy standard streams.
class ApplicationTest : public testing::ApplicationTestBase {
protected:
ApplicationTest() : cmd_line_(base::FilePath(L"test.exe")) {
}
template<typename App>
void RerouteAppStreams(App* app) {
ASSERT_TRUE(app != NULL);
// Validate that app's streams were initialized properly.
ASSERT_EQ(stdin, app->in());
ASSERT_EQ(stdout, app->out());
ASSERT_EQ(stderr, app->err());
// Route test_app_ streams to
app->set_in(in());
app->set_out(out());
app->set_err(err());
ASSERT_EQ(in(), app->in());
ASSERT_EQ(out(), app->out());
ASSERT_EQ(err(), app->err());
}
virtual void SetUp() override {
testing::Test::SetUp();
// Validate test streams were created.
ASSERT_TRUE(in() != NULL);
ASSERT_TRUE(out() != NULL);
ASSERT_TRUE(err() != NULL);
// Reroute the test application streams.
RerouteAppStreams(&test_app_);
RerouteAppStreams(&mock_app_);
}
base::CommandLine cmd_line_;
TestApp test_app_;
MockApp mock_app_;
};
bool CreateEmptyFile(const base::FilePath& path) {
base::ScopedFILE f(base::OpenFile(path, "wb"));
if (f.get() == NULL)
return false;
return true;
}
} // namespace
TEST_F(ApplicationTest, AppImplBaseDefault) {
// The command line for this process has already been set we can pass
// whatever we want to Main and it will end up using the current
// command line.
const base::CommandLine* current_command_line =
base::CommandLine::ForCurrentProcess();
ASSERT_TRUE(current_command_line != NULL);
// Validate the application name.
EXPECT_EQ(kTestAppImplName, test_app_.name());
// Check the default command line and streams.
EXPECT_EQ(current_command_line, test_app_.command_line());
// Validate the accessors.
test_app_.set_command_line(&cmd_line_);
EXPECT_EQ(&cmd_line_, test_app_.command_line());
EXPECT_EQ(0, test_app_.Run());
}
TEST_F(ApplicationTest, AppImplBaseVerbosity) {
ScopedLogLevelSaver log_level_saver;
cmd_line_.AppendSwitchASCII("verbose", "2");
test_app_.set_command_line(&cmd_line_);
ASSERT_EQ(0, test_app_.Run());
ASSERT_EQ(-2, logging::GetMinLogLevel());
}
TEST_F(ApplicationTest, MockAppFailsCommandLineParsing) {
MockAppImpl& mock_impl = mock_app_.implementation();
EXPECT_CALL(mock_impl, ParseCommandLine(_))
.WillOnce(Return(false));
EXPECT_NE(0, mock_app_.Run());
}
TEST_F(ApplicationTest, MockAppFailsSetup) {
MockAppImpl& mock_impl = mock_app_.implementation();
EXPECT_CALL(mock_impl, ParseCommandLine(_))
.WillOnce(Return(true));
EXPECT_CALL(mock_impl, SetUp())
.WillOnce(Return(false));
EXPECT_NE(0, mock_app_.Run());
}
TEST_F(ApplicationTest, MockAppFailsRun) {
MockAppImpl& mock_impl = mock_app_.implementation();
EXPECT_CALL(mock_impl, ParseCommandLine(_))
.WillOnce(Return(true));
EXPECT_CALL(mock_impl, SetUp())
.WillOnce(Return(true));
EXPECT_CALL(mock_impl, Run())
.WillOnce(Return(2));
EXPECT_CALL(mock_impl, TearDown());
EXPECT_EQ(2, mock_app_.Run());
}
TEST_F(ApplicationTest, AbsolutePath) {
base::FilePath current_dir;
ASSERT_TRUE(base::GetCurrentDirectory(&current_dir));
const base::FilePath kRelativePath(L"foo\\bar\\file.txt");
const base::FilePath kAbsolutePath(current_dir.Append(kRelativePath));
EXPECT_EQ(base::FilePath(),
test_app_.implementation().AbsolutePath(base::FilePath()));
EXPECT_EQ(kAbsolutePath,
test_app_.implementation().AbsolutePath(kRelativePath));
EXPECT_EQ(kAbsolutePath,
test_app_.implementation().AbsolutePath(kAbsolutePath));
}
TEST_F(ApplicationTest, AppendMatchingPaths) {
// Create some files to match against.
base::FilePath temp_dir;
ASSERT_NO_FATAL_FAILURE(CreateTemporaryDir(&temp_dir));
ASSERT_TRUE(CreateEmptyFile(temp_dir.Append(L"a.txt")));
ASSERT_TRUE(CreateEmptyFile(temp_dir.Append(L"b.txt")));
ASSERT_TRUE(CreateEmptyFile(temp_dir.Append(L"c.txt")));
ASSERT_TRUE(CreateEmptyFile(temp_dir.Append(L"a.bin")));
// Should get false on no match.
std::vector<base::FilePath> no_matching_paths;
ASSERT_FALSE(TestAppImpl::AppendMatchingPaths(temp_dir.Append(L"d.*"),
&no_matching_paths));
EXPECT_TRUE(no_matching_paths.empty());
// Match a pattern where the extension is a wildcard.
std::vector<base::FilePath> a_star_paths;
ASSERT_TRUE(TestAppImpl::AppendMatchingPaths(temp_dir.Append(L"a.*"),
&a_star_paths));
EXPECT_THAT(a_star_paths,
testing::ElementsAre(temp_dir.Append(L"a.bin"),
temp_dir.Append(L"a.txt")));
// Match a pattern where the extension is set but the root name is a wildcard.
std::vector<base::FilePath> star_txt_paths;
ASSERT_TRUE(TestAppImpl::AppendMatchingPaths(temp_dir.Append(L"*.txt"),
&star_txt_paths));
EXPECT_THAT(star_txt_paths,
testing::ElementsAre(temp_dir.Append(L"a.txt"),
temp_dir.Append(L"b.txt"),
temp_dir.Append(L"c.txt")));
}
TEST_F(ApplicationTest, GetDeprecatedSwitch) {
const std::string kFoo("foo");
const std::string kBar("bar");
const std::string kMissing("missing");
const base::FilePath kFooPath(L"C:\\foo");
const base::FilePath kBarPath(L"C:\\bar");
ASSERT_NE(kFoo, kBar);
ASSERT_NE(kFooPath, kBarPath);
base::FilePath path;
EXPECT_TRUE(TestAppImpl::GetDeprecatedSwitch(
&cmd_line_, kFoo, kBar, &base::CommandLine::GetSwitchValuePath, &path));
EXPECT_TRUE(path.empty());
cmd_line_.AppendSwitchPath(kFoo, kFooPath);
cmd_line_.AppendSwitchPath(kBar, kBarPath);
EXPECT_FALSE(TestAppImpl::GetDeprecatedSwitch(
&cmd_line_, kFoo, kBar, &base::CommandLine::GetSwitchValuePath, &path));
EXPECT_TRUE(TestAppImpl::GetDeprecatedSwitch(
&cmd_line_, kFoo, kMissing, &base::CommandLine::GetSwitchValuePath,
&path));
EXPECT_EQ(kFooPath, path);
EXPECT_TRUE(TestAppImpl::GetDeprecatedSwitch(
&cmd_line_, kMissing, kBar, &base::CommandLine::GetSwitchValuePath,
&path));
EXPECT_EQ(kBarPath, path);
}
} // namespace application