1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/common/sandbox_mac_unittest_helper.h"
6
7extern "C" {
8#include <sandbox.h>
9}
10
11#include <map>
12
13#include "base/files/file_path.h"
14#include "base/logging.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/process/kill.h"
17#include "content/common/sandbox_mac.h"
18#include "content/test/test_content_client.h"
19#include "testing/multiprocess_func_list.h"
20
21namespace content {
22namespace {
23
24const char* kSandboxTypeKey = "CHROMIUM_SANDBOX_SANDBOX_TYPE";
25const char* kSandboxTestNameKey = "CHROMIUM_SANDBOX_TEST_NAME";
26const char* kTestDataKey = "CHROMIUM_SANDBOX_USER_DATA";
27
28}  // namespace
29
30// Support infrastructure for REGISTER_SANDBOX_TEST_CASE macro.
31namespace internal {
32
33typedef std::map<std::string,MacSandboxTestCase*> SandboxTestMap;
34
35// A function that returns a common map from string -> test case class.
36SandboxTestMap& GetSandboxTestMap() {
37  static SandboxTestMap test_map;
38  return test_map;
39}
40
41void AddSandboxTestCase(const char* test_name, MacSandboxTestCase* test_class) {
42  SandboxTestMap& test_map = GetSandboxTestMap();
43  if (test_map.find(test_name) != test_map.end()) {
44    LOG(ERROR) << "Trying to register duplicate test" << test_name;
45    NOTREACHED();
46  }
47  test_map[test_name] = test_class;
48}
49
50}  // namespace internal
51
52bool MacSandboxTest::RunTestInAllSandboxTypes(const char* test_name,
53                                              const char* test_data) {
54  // Go through all the sandbox types, and run the test case in each of them
55  // if one fails, abort.
56  for(int i = static_cast<int>(SANDBOX_TYPE_FIRST_TYPE);
57      i < SANDBOX_TYPE_AFTER_LAST_TYPE;
58      ++i) {
59    if (!RunTestInSandbox(static_cast<SandboxType>(i),
60            test_name, test_data)) {
61      LOG(ERROR) << "Sandboxed test (" << test_name << ")" <<
62          "Failed in sandbox type " << i <<
63          "user data: (" << test_data << ")";
64      return false;
65    }
66  }
67 return true;
68}
69
70bool MacSandboxTest::RunTestInSandbox(SandboxType sandbox_type,
71                                      const char* test_name,
72                                      const char* test_data) {
73  std::stringstream s;
74  s << static_cast<int>(static_cast<int>(sandbox_type));
75  setenv(kSandboxTypeKey, s.str().c_str(), 1);
76  setenv(kSandboxTestNameKey, test_name, 1);
77  if (test_data)
78    setenv(kTestDataKey, test_data, 1);
79
80  base::ProcessHandle child_process = SpawnChild("mac_sandbox_test_runner");
81  if (child_process == base::kNullProcessHandle) {
82    LOG(WARNING) << "SpawnChild failed";
83    return false;
84  }
85  int code = -1;
86  if (!base::WaitForExitCode(child_process, &code)) {
87    LOG(WARNING) << "base::WaitForExitCode failed";
88    return false;
89  }
90  return code == 0;
91}
92
93bool MacSandboxTestCase::BeforeSandboxInit() {
94  return true;
95}
96
97void MacSandboxTestCase::SetTestData(const char* test_data) {
98  test_data_ = test_data;
99}
100
101// Given a test name specified by |name| return that test case.
102// If no test case is found for the given name, return NULL.
103MacSandboxTestCase *SandboxTestForName(const char* name) {
104  using internal::SandboxTestMap;
105  using internal::GetSandboxTestMap;
106
107  SandboxTestMap all_tests = GetSandboxTestMap();
108
109  SandboxTestMap::iterator it = all_tests.find(name);
110  if (it == all_tests.end()) {
111    LOG(ERROR) << "Couldn't find sandbox test case(" << name << ")";
112    return NULL;
113  }
114
115  return it->second;
116}
117
118// Main function for driver process that enables the sandbox and runs test
119// code.
120MULTIPROCESS_TEST_MAIN(mac_sandbox_test_runner) {
121  TestContentClient content_client;
122  SetContentClient(&content_client);
123  // Extract parameters.
124  char* sandbox_type_str = getenv(kSandboxTypeKey);
125  if (!sandbox_type_str) {
126    LOG(ERROR) << "Sandbox type not specified";
127    return -1;
128  }
129  SandboxType sandbox_type = static_cast<SandboxType>(atoi(sandbox_type_str));
130  char* sandbox_test_name = getenv(kSandboxTestNameKey);
131  if (!sandbox_test_name) {
132    LOG(ERROR) << "Sandbox test name not specified";
133    return -1;
134  }
135
136  const char* test_data = getenv(kTestDataKey);
137
138  // Find Test Function to run;
139  scoped_ptr<MacSandboxTestCase>
140      test_case(SandboxTestForName(sandbox_test_name));
141  if (!test_case) {
142    LOG(ERROR) << "Invalid sandbox test name (" << sandbox_test_name << ")";
143    return -1;
144  }
145  if (test_data)
146    test_case->SetTestData(test_data);
147
148  // Run Test.
149  if (!test_case->BeforeSandboxInit()) {
150    LOG(ERROR) << sandbox_test_name << "Failed test before sandbox init";
151    return -1;
152  }
153
154  Sandbox::SandboxWarmup(sandbox_type);
155
156  if (!Sandbox::EnableSandbox(sandbox_type, base::FilePath())) {
157    LOG(ERROR) << "Failed to initialize sandbox " << sandbox_type;
158    return -1;
159  }
160
161  if (!test_case->SandboxedTest()) {
162    LOG(ERROR) << sandbox_test_name << "Failed sandboxed test";
163    return -1;
164  }
165
166  return 0;
167}
168
169}  // namespace content
170