1// Copyright (c) 2012 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#ifndef CONTENT_COMMON_SANDBOX_MAC_UNITTEST_HELPER_H_
6#define CONTENT_COMMON_SANDBOX_MAC_UNITTEST_HELPER_H_
7
8#include "base/test/multiprocess_test.h"
9#include "content/common/sandbox_mac.h"
10
11namespace content {
12
13// Helpers for writing unit tests that runs in the context of the Mac sandbox.
14//
15// How to write a sandboxed test:
16// 1. Create a class that inherits from MacSandboxTestCase and overrides
17// its functions to run code before or after the sandbox is initialised in a
18// subprocess.
19// 2. Register the class you just created with the REGISTER_SANDBOX_TEST_CASE()
20// macro.
21// 3. Write a test [using TEST_F()] that inherits from MacSandboxTest and call
22// one of its helper functions to launch the test.
23//
24// Example:
25//  class TestCaseThatRunsInSandboxedSubprocess : public MacSandboxTestCase {
26//   public:
27//    virtual bool SandboxedTest() {
28//      .. test code that runs in sandbox goes here ..
29//      return true; // always succeed.
30//    }
31//  };
32//
33//  // Register the test case you just created.
34//  REGISTER_SANDBOX_TEST_CASE(TestCaseThatRunsInSandboxedSubprocess);
35//
36//  TEST_F(MacSandboxTest, ATest) {
37//    EXPECT_TRUE(RunTestInAllSandboxTypes(
38//                    "TestCaseThatRunsInSandboxedSubprocess",
39//                    NULL));
40//  }
41
42// Base test type with helper functions to spawn a subprocess that exercises
43// a given test in the sandbox.
44class MacSandboxTest : public base::MultiProcessTest {
45 public:
46  // Runs a test specified by |test_name| in a sandbox of the type specified
47  // by |sandbox_type|. |test_data| is a custom string that a test can pass
48  // to the child process runing in the sandbox, or NULL if additional data is
49  // required.
50  // Returns true if the test passes, false if either of the functions in
51  // the corresponding MacSandboxTestCase return false.
52  bool RunTestInSandbox(content::SandboxType sandbox_type,
53                        const char* test_name,
54                        const char* test_data);
55
56  // Runs the test specified by |test_name| in all the different sandbox types
57  // known to content, one by one.
58  // Returns true if the test passes, false if either of the functions in
59  // the corresponding MacSandboxTestCase return false in any of the spawned
60  // processes.
61  //
62  // DANGER DANGER DANGER:
63  // Additional sandbox types defined by the embedder (e.g. the NaCl sandbox)
64  // won't be covered by these tests.
65  bool RunTestInAllSandboxTypes(const char* test_name,
66                                const char* test_data);
67};
68
69// Class to ease writing test cases that run inside the OS X sandbox.
70// This class is instantiated in a subprocess, and allows you to run test code
71// at various stages of execution.
72// Note that you must register the subclass you create with the
73// REGISTER_SANDBOX_TEST_CASE so it's visible to the test driver.
74class MacSandboxTestCase {
75 public:
76  virtual ~MacSandboxTestCase() {}
77
78  // Code that runs in the sandboxed subprocess before the sandbox is
79  // initialized.
80  // Returning false from this function will cause the entire test case to fail.
81  virtual bool BeforeSandboxInit();
82
83  // Code that runs in the sandboxed subprocess when the sandbox has been
84  // enabled.
85  // Returning false from this function will cause the entire test case to fail.
86  virtual bool SandboxedTest() = 0;
87
88  // The data that's passed in the |user_data| parameter of
89  // RunTest[s]InSandbox() is passed to this function.
90  virtual void SetTestData(const char* test_data);
91
92 protected:
93  std::string test_data_;
94};
95
96// Plumbing to support the REGISTER_SANDBOX_TEST_CASE macro.
97namespace internal {
98
99// Register a test case with a given name.
100void AddSandboxTestCase(const char* test_name, MacSandboxTestCase* test_class);
101
102// Construction of this class causes a new entry to be placed in a global
103// map.
104template <class T> struct RegisterSandboxTest {
105  RegisterSandboxTest(const char* test_name) {
106    AddSandboxTestCase(test_name, new T);
107  }
108};
109
110#define REGISTER_SANDBOX_TEST_CASE(class_name) \
111  namespace { \
112    content::internal::RegisterSandboxTest<class_name> \
113      register_test##class_name(#class_name); \
114  }  // namespace
115
116}  // namespace internal
117
118}  // namespace content
119
120#endif  // CONTENT_COMMON_SANDBOX_MAC_UNITTEST_HELPER_H_
121