michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: // This file contains unit tests for the job object. michael@0: michael@0: #include "base/win/scoped_process_information.h" michael@0: #include "sandbox/win/src/job.h" michael@0: #include "testing/gtest/include/gtest/gtest.h" michael@0: michael@0: namespace sandbox { michael@0: michael@0: // Tests the creation and destruction of the job. michael@0: TEST(JobTest, TestCreation) { michael@0: // Scope the creation of Job. michael@0: { michael@0: // Create the job. michael@0: Job job; michael@0: ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); michael@0: michael@0: // check if the job exists. michael@0: HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, michael@0: L"my_test_job_name"); michael@0: ASSERT_TRUE(job_handle != NULL); michael@0: michael@0: if (job_handle) michael@0: CloseHandle(job_handle); michael@0: } michael@0: michael@0: // Check if the job is destroyed when the object goes out of scope. michael@0: HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name"); michael@0: ASSERT_TRUE(job_handle == NULL); michael@0: ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); michael@0: } michael@0: michael@0: // Tests the method "Detach". michael@0: TEST(JobTest, TestDetach) { michael@0: HANDLE job_handle; michael@0: // Scope the creation of Job. michael@0: { michael@0: // Create the job. michael@0: Job job; michael@0: ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); michael@0: michael@0: job_handle = job.Detach(); michael@0: ASSERT_TRUE(job_handle != NULL); michael@0: } michael@0: michael@0: // Check to be sure that the job is still alive even after the object is gone michael@0: // out of scope. michael@0: HANDLE job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, FALSE, michael@0: L"my_test_job_name"); michael@0: ASSERT_TRUE(job_handle_dup != NULL); michael@0: michael@0: // Remove all references. michael@0: if (job_handle_dup) michael@0: ::CloseHandle(job_handle_dup); michael@0: michael@0: if (job_handle) michael@0: ::CloseHandle(job_handle); michael@0: michael@0: // Check if the jbo is really dead. michael@0: job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name"); michael@0: ASSERT_TRUE(job_handle == NULL); michael@0: ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); michael@0: } michael@0: michael@0: // Tests the ui exceptions michael@0: TEST(JobTest, TestExceptions) { michael@0: HANDLE job_handle; michael@0: // Scope the creation of Job. michael@0: { michael@0: // Create the job. michael@0: Job job; michael@0: ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", michael@0: JOB_OBJECT_UILIMIT_READCLIPBOARD)); michael@0: michael@0: job_handle = job.Detach(); michael@0: ASSERT_TRUE(job_handle != NULL); michael@0: michael@0: JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; michael@0: DWORD size = sizeof(jbur); michael@0: BOOL result = ::QueryInformationJobObject(job_handle, michael@0: JobObjectBasicUIRestrictions, michael@0: &jbur, size, &size); michael@0: ASSERT_TRUE(result); michael@0: michael@0: ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, 0); michael@0: ::CloseHandle(job_handle); michael@0: } michael@0: michael@0: // Scope the creation of Job. michael@0: { michael@0: // Create the job. michael@0: Job job; michael@0: ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); michael@0: michael@0: job_handle = job.Detach(); michael@0: ASSERT_TRUE(job_handle != NULL); michael@0: michael@0: JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; michael@0: DWORD size = sizeof(jbur); michael@0: BOOL result = ::QueryInformationJobObject(job_handle, michael@0: JobObjectBasicUIRestrictions, michael@0: &jbur, size, &size); michael@0: ASSERT_TRUE(result); michael@0: michael@0: ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, michael@0: JOB_OBJECT_UILIMIT_READCLIPBOARD); michael@0: ::CloseHandle(job_handle); michael@0: } michael@0: } michael@0: michael@0: // Tests the error case when the job is initialized twice. michael@0: TEST(JobTest, DoubleInit) { michael@0: // Create the job. michael@0: Job job; michael@0: ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0)); michael@0: ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0)); michael@0: } michael@0: michael@0: // Tests the error case when we use a method and the object is not yet michael@0: // initialized. michael@0: TEST(JobTest, NoInit) { michael@0: Job job; michael@0: ASSERT_EQ(ERROR_NO_DATA, job.UserHandleGrantAccess(NULL)); michael@0: ASSERT_EQ(ERROR_NO_DATA, job.AssignProcessToJob(NULL)); michael@0: ASSERT_TRUE(job.Detach() == NULL); michael@0: } michael@0: michael@0: // Tests the initialization of the job with different security level. michael@0: TEST(JobTest, SecurityLevel) { michael@0: Job job1; michael@0: ASSERT_EQ(ERROR_SUCCESS, job1.Init(JOB_LOCKDOWN, L"job1", 0)); michael@0: michael@0: Job job2; michael@0: ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0)); michael@0: michael@0: Job job3; michael@0: ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0)); michael@0: michael@0: Job job4; michael@0: ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0)); michael@0: michael@0: Job job5; michael@0: ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0)); michael@0: michael@0: // JOB_NONE means we run without a job object so Init should fail. michael@0: Job job6; michael@0: ASSERT_EQ(ERROR_BAD_ARGUMENTS, job6.Init(JOB_NONE, L"job6", 0)); michael@0: michael@0: Job job7; michael@0: ASSERT_EQ(ERROR_BAD_ARGUMENTS, job7.Init( michael@0: static_cast(JOB_NONE+1), L"job7", 0)); michael@0: } michael@0: michael@0: // Tests the method "AssignProcessToJob". michael@0: TEST(JobTest, ProcessInJob) { michael@0: // Create the job. michael@0: Job job; michael@0: ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_UNPROTECTED, L"job_test_process", 0)); michael@0: michael@0: BOOL result = FALSE; michael@0: michael@0: wchar_t notepad[] = L"notepad"; michael@0: STARTUPINFO si = { sizeof(si) }; michael@0: base::win::ScopedProcessInformation pi; michael@0: result = ::CreateProcess(NULL, notepad, NULL, NULL, FALSE, 0, NULL, NULL, &si, michael@0: pi.Receive()); michael@0: ASSERT_TRUE(result); michael@0: ASSERT_EQ(ERROR_SUCCESS, job.AssignProcessToJob(pi.process_handle())); michael@0: michael@0: // Get the job handle. michael@0: HANDLE job_handle = job.Detach(); michael@0: michael@0: // Check if the process is in the job. michael@0: JOBOBJECT_BASIC_PROCESS_ID_LIST jbpidl = {0}; michael@0: DWORD size = sizeof(jbpidl); michael@0: result = ::QueryInformationJobObject(job_handle, michael@0: JobObjectBasicProcessIdList, michael@0: &jbpidl, size, &size); michael@0: EXPECT_TRUE(result); michael@0: michael@0: EXPECT_EQ(1, jbpidl.NumberOfAssignedProcesses); michael@0: EXPECT_EQ(1, jbpidl.NumberOfProcessIdsInList); michael@0: EXPECT_EQ(pi.process_id(), jbpidl.ProcessIdList[0]); michael@0: michael@0: EXPECT_TRUE(::TerminateProcess(pi.process_handle(), 0)); michael@0: michael@0: EXPECT_TRUE(::CloseHandle(job_handle)); michael@0: } michael@0: michael@0: } // namespace sandbox