mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 04:09:50 +00:00
Bug 1652156 - Add RDD Process Testing to the Sandbox Testing Framework. r=handyman,mattwoodrow
Differential Revision: https://phabricator.services.mozilla.com/D116894
This commit is contained in:
parent
6cacc3a511
commit
426e10f0e2
@ -11,6 +11,10 @@ include protocol PProfiler;
|
||||
include protocol PRemoteDecoderManager;
|
||||
include protocol PVideoBridge;
|
||||
|
||||
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
|
||||
include protocol PSandboxTesting;
|
||||
#endif
|
||||
|
||||
using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h";
|
||||
[MoveOnly] using mozilla::UntrustedModulesData from "mozilla/UntrustedModulesData.h";
|
||||
[MoveOnly] using mozilla::ModulePaths from "mozilla/UntrustedModulesData.h";
|
||||
@ -51,6 +55,10 @@ parent:
|
||||
|
||||
async GetUntrustedModulesData() returns (UntrustedModulesData? data);
|
||||
|
||||
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
|
||||
async InitSandboxTesting(Endpoint<PSandboxTestingChild> aEndpoint);
|
||||
#endif
|
||||
|
||||
child:
|
||||
|
||||
async InitCrashReporter(NativeThreadId threadId);
|
||||
|
@ -50,6 +50,10 @@
|
||||
#include "nsDebugImpl.h"
|
||||
#include "nsThreadManager.h"
|
||||
|
||||
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
|
||||
# include "mozilla/SandboxTestingChild.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace ipc;
|
||||
@ -253,6 +257,17 @@ mozilla::ipc::IPCResult RDDParent::RecvPreferenceUpdate(const Pref& aPref) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
|
||||
mozilla::ipc::IPCResult RDDParent::RecvInitSandboxTesting(
|
||||
Endpoint<PSandboxTestingChild>&& aEndpoint) {
|
||||
if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
|
||||
return IPC_FAIL(
|
||||
this, "InitSandboxTesting failed to initialise the child process.");
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
#endif
|
||||
|
||||
void RDDParent::ActorDestroy(ActorDestroyReason aWhy) {
|
||||
if (AbnormalShutdown == aWhy) {
|
||||
NS_WARNING("Shutting down RDD process early due to a crash!");
|
||||
|
@ -49,6 +49,10 @@ class RDDParent final : public PRDDParent {
|
||||
mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& pref);
|
||||
mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref);
|
||||
|
||||
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
|
||||
mozilla::ipc::IPCResult RecvInitSandboxTesting(
|
||||
Endpoint<PSandboxTestingChild>&& aEndpoint);
|
||||
#endif
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
private:
|
||||
|
@ -7,11 +7,14 @@
|
||||
|
||||
IPDL_SOURCES += [
|
||||
"PMediaDecoderParams.ipdlh",
|
||||
"PRDD.ipdl",
|
||||
"PRemoteDecoder.ipdl",
|
||||
"PRemoteDecoderManager.ipdl",
|
||||
]
|
||||
|
||||
PREPROCESSED_IPDL_SOURCES += [
|
||||
"PRDD.ipdl",
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
"RDDChild.h",
|
||||
"RDDParent.h",
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "mozilla/gfx/GPUProcessManager.h"
|
||||
#include "mozilla/gfx/GPUChild.h"
|
||||
#include "mozilla/net/SocketProcessParent.h"
|
||||
#include "mozilla/RDDProcessManager.h"
|
||||
#include "mozilla/RDDChild.h"
|
||||
#include "mozilla/ipc/Endpoint.h"
|
||||
#include "nsIOService.h"
|
||||
|
||||
@ -25,6 +27,18 @@ namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(SandboxTest, mozISandboxTest)
|
||||
|
||||
inline void UnsetEnvVariable(const nsCString& aEnvVarName) {
|
||||
nsCString aEnvVarNameFull = aEnvVarName + "="_ns;
|
||||
int rv_unset =
|
||||
#ifdef XP_UNIX
|
||||
unsetenv(aEnvVarName.get());
|
||||
#endif // XP_UNIX
|
||||
#ifdef XP_WIN
|
||||
_putenv(aEnvVarNameFull.get());
|
||||
#endif // XP_WIN
|
||||
MOZ_ASSERT(rv_unset == 0, "Error unsetting env var");
|
||||
}
|
||||
|
||||
GeckoProcessType GeckoProcessStringToType(const nsCString& aString) {
|
||||
for (GeckoProcessType type = GeckoProcessType(0);
|
||||
type < GeckoProcessType::GeckoProcessType_End;
|
||||
@ -61,62 +75,77 @@ SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
RefPtr<ProcessPromise::Private> mProcessPromise =
|
||||
MakeRefPtr<ProcessPromise::Private>(__func__);
|
||||
|
||||
switch (type) {
|
||||
case GeckoProcessType_Content: {
|
||||
nsTArray<ContentParent*> parents;
|
||||
ContentParent::GetAll(parents);
|
||||
MOZ_ASSERT(parents.Length() > 0);
|
||||
mSandboxTestingParents[type] =
|
||||
InitializeSandboxTestingActors(parents[0]);
|
||||
if (parents[0]) {
|
||||
mProcessPromise->Resolve(
|
||||
std::move(InitializeSandboxTestingActors(parents[0])), __func__);
|
||||
} else {
|
||||
mProcessPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get Content process");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GeckoProcessType_GPU: {
|
||||
gfx::GPUProcessManager* gpuProc = gfx::GPUProcessManager::Get();
|
||||
gfx::GPUChild* gpuChild = gpuProc ? gpuProc->GetGPUChild() : nullptr;
|
||||
if (!gpuChild) {
|
||||
// There is no GPU process for this OS. Report test done.
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(observerService);
|
||||
observerService->NotifyObservers(nullptr, "sandbox-test-done", 0);
|
||||
return NS_OK;
|
||||
if (gpuChild) {
|
||||
mProcessPromise->Resolve(
|
||||
std::move(InitializeSandboxTestingActors(gpuChild)), __func__);
|
||||
} else {
|
||||
mProcessPromise->Reject(NS_OK, __func__);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
mSandboxTestingParents[type] = InitializeSandboxTestingActors(gpuChild);
|
||||
case GeckoProcessType_RDD: {
|
||||
RDDProcessManager* rddProc = RDDProcessManager::Get();
|
||||
rddProc->LaunchRDDProcess()->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[mProcessPromise, rddProc]() {
|
||||
RDDChild* rddChild = rddProc ? rddProc->GetRDDChild() : nullptr;
|
||||
if (rddChild) {
|
||||
return mProcessPromise->Resolve(
|
||||
std::move(InitializeSandboxTestingActors(rddChild)),
|
||||
__func__);
|
||||
}
|
||||
return mProcessPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
},
|
||||
[mProcessPromise](nsresult aError) {
|
||||
MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get RDD process");
|
||||
return mProcessPromise->Reject(aError, __func__);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case GeckoProcessType_Socket: {
|
||||
// mochitest harness force this variable, but we actually do not want
|
||||
// that
|
||||
int rv_unset =
|
||||
#ifdef XP_UNIX
|
||||
unsetenv("MOZ_DISABLE_SOCKET_PROCESS");
|
||||
#endif // XP_UNIX
|
||||
#ifdef XP_WIN
|
||||
_putenv("MOZ_DISABLE_SOCKET_PROCESS=");
|
||||
#endif // XP_WIN
|
||||
MOZ_ASSERT(rv_unset == 0, "Error unsetting env var");
|
||||
UnsetEnvVariable("MOZ_DISABLE_SOCKET_PROCESS"_ns);
|
||||
|
||||
nsresult rv_pref =
|
||||
Preferences::SetBool("network.process.enabled", true);
|
||||
MOZ_ASSERT(rv_pref == NS_OK, "Error enforcing pref");
|
||||
|
||||
MOZ_ASSERT(net::gIOService, "No gIOService?");
|
||||
RefPtr<SandboxTest> self = this;
|
||||
net::gIOService->CallOrWaitForSocketProcess([self, type]() {
|
||||
|
||||
net::gIOService->CallOrWaitForSocketProcess([mProcessPromise]() {
|
||||
// If socket process was previously disabled by env,
|
||||
// nsIOService code will take some time before it creates the new
|
||||
// process and it triggers this callback
|
||||
//
|
||||
// If that happens, then by the time we reach the end of StartTests()
|
||||
// the mSandboxTestingParents[type] might not yet have been updated
|
||||
// this is why below we allow for a delayed dispatch to give a chance
|
||||
net::SocketProcessParent* parent =
|
||||
net::SocketProcessParent::GetSingleton();
|
||||
self->mSandboxTestingParents[type] =
|
||||
InitializeSandboxTestingActors(parent);
|
||||
if (parent) {
|
||||
return mProcessPromise->Resolve(
|
||||
std::move(InitializeSandboxTestingActors(parent)), __func__);
|
||||
}
|
||||
return mProcessPromise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -127,26 +156,27 @@ SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
if (!mSandboxTestingParents[type]) {
|
||||
if (type == GeckoProcessType_Socket) {
|
||||
// Give a chance to the socket process to be present and to the callback
|
||||
// above to be ran. We delay by 5 seconds, but hopefully since it is all
|
||||
// on the main thread, the value itself should not be important.
|
||||
RefPtr<SandboxTest> self = this;
|
||||
NS_DelayedDispatchToCurrentThread(
|
||||
NS_NewRunnableFunction(
|
||||
"SandboxTest::StartTests",
|
||||
[self, type]() {
|
||||
if (!self->mSandboxTestingParents[type]) {
|
||||
MOZ_ASSERT_UNREACHABLE(
|
||||
"SandboxTest failed to get a Parent");
|
||||
}
|
||||
}),
|
||||
5e3 /* delay by five seconds */);
|
||||
} else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
RefPtr<SandboxTest> self = this;
|
||||
RefPtr<ProcessPromise> aPromise(mProcessPromise);
|
||||
aPromise->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[self, type](SandboxTestingParent* aValue) {
|
||||
self->mSandboxTestingParents[type] = std::move(aValue);
|
||||
return NS_OK;
|
||||
},
|
||||
[](nsresult aError) {
|
||||
if (aError == NS_OK) {
|
||||
// There is no such process for this OS. Report test done.
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(observerService);
|
||||
observerService->NotifyObservers(nullptr, "sandbox-test-done",
|
||||
nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get a process");
|
||||
return NS_ERROR_FAILURE;
|
||||
});
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -9,12 +9,14 @@
|
||||
#include "SandboxTestingParent.h"
|
||||
#include "mozISandboxTest.h"
|
||||
#include "mozilla/GfxMessageUtils.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
|
||||
#if !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
|
||||
# error "This file should not be used outside of debug with tests"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class SandboxTest : public mozISandboxTest {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -22,6 +24,11 @@ class SandboxTest : public mozISandboxTest {
|
||||
|
||||
SandboxTest() : mSandboxTestingParents{nullptr} {};
|
||||
|
||||
// We allow nsresult to be rejected with values:
|
||||
// - NS_ERROR_FAILURE in obvious case of error
|
||||
// - NS_OK in case of success to complete the code but missing process (GPU)
|
||||
using ProcessPromise = MozPromise<SandboxTestingParent*, nsresult, true>;
|
||||
|
||||
private:
|
||||
virtual ~SandboxTest() = default;
|
||||
static constexpr size_t NumProcessTypes =
|
||||
|
@ -66,6 +66,10 @@ void SandboxTestingChild::Bind(Endpoint<PSandboxTestingChild>&& aEndpoint) {
|
||||
RunTestsContent(this);
|
||||
}
|
||||
|
||||
if (XRE_IsRDDProcess()) {
|
||||
RunTestsRDD(this);
|
||||
}
|
||||
|
||||
if (XRE_IsSocketProcess()) {
|
||||
RunTestsSocket(this);
|
||||
}
|
||||
@ -96,6 +100,12 @@ bool SandboxTestingChild::RecvShutDown() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SandboxTestingChild::ReportNoTests() {
|
||||
SendReportTestResults("dummy_test"_ns, /* shouldSucceed */ true,
|
||||
/* didSucceed */ true,
|
||||
"The test framework fails if there are no cases."_ns);
|
||||
}
|
||||
|
||||
#ifdef XP_UNIX
|
||||
template <typename F>
|
||||
void SandboxTestingChild::ErrnoTest(const nsCString& aName, bool aExpectSuccess,
|
||||
@ -104,6 +114,14 @@ void SandboxTestingChild::ErrnoTest(const nsCString& aName, bool aExpectSuccess,
|
||||
PosixTest(aName, aExpectSuccess, status);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void SandboxTestingChild::ErrnoValueTest(const nsCString& aName,
|
||||
bool aExpectEquals, int aExpectedErrno,
|
||||
F&& aFunction) {
|
||||
int status = aFunction() >= 0 ? 0 : errno;
|
||||
PosixTest(aName, aExpectEquals, status == aExpectedErrno);
|
||||
}
|
||||
|
||||
void SandboxTestingChild::PosixTest(const nsCString& aName, bool aExpectSuccess,
|
||||
int aStatus) {
|
||||
bool succeeded = aStatus == 0;
|
||||
|
@ -40,6 +40,11 @@ class SandboxTestingChild : public PSandboxTestingChild {
|
||||
|
||||
virtual bool RecvShutDown();
|
||||
|
||||
// Helper to return that no test have been executed. Tests should make sure
|
||||
// they have some fallback through that otherwise the framework will consider
|
||||
// absence of test report as a failure.
|
||||
inline void ReportNoTests();
|
||||
|
||||
#ifdef XP_UNIX
|
||||
// For test cases that return an error number or 0, like newer POSIX APIs.
|
||||
void PosixTest(const nsCString& aName, bool aExpectSuccess, int aStatus);
|
||||
@ -49,11 +54,18 @@ class SandboxTestingChild : public PSandboxTestingChild {
|
||||
// is used only in this function call (so `[&]` captures are safe).
|
||||
template <typename F>
|
||||
void ErrnoTest(const nsCString& aName, bool aExpectSuccess, F&& aFunction);
|
||||
|
||||
// Similar to ErrnoTest, except that we want to compare a specific `errno`
|
||||
// being returned (or not).
|
||||
template <typename F>
|
||||
void ErrnoValueTest(const nsCString& aName, bool aExpectEquals,
|
||||
int aExpectedErrno, F&& aFunction);
|
||||
#endif
|
||||
|
||||
private:
|
||||
explicit SandboxTestingChild(SandboxTestingThread* aThread,
|
||||
Endpoint<PSandboxTestingChild>&& aEndpoint);
|
||||
|
||||
void Bind(Endpoint<PSandboxTestingChild>&& aEndpoint);
|
||||
|
||||
UniquePtr<SandboxTestingThread> mThread;
|
||||
|
@ -13,6 +13,10 @@
|
||||
# include <netdb.h>
|
||||
# ifdef XP_LINUX
|
||||
# include <sys/prctl.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <termios.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/time.h>
|
||||
# endif // XP_LINUX
|
||||
# include <sys/socket.h>
|
||||
# include <sys/stat.h>
|
||||
@ -50,11 +54,7 @@ void RunTestsContent(SandboxTestingChild* child) {
|
||||
[&] { return clock_getres(CLOCK_REALTIME, &res); });
|
||||
|
||||
#else // XP_UNIX
|
||||
child->SendReportTestResults(
|
||||
"dummy_test"_ns,
|
||||
/* shouldSucceed */ true,
|
||||
/* didSucceed */ true,
|
||||
"The test framework fails if there are no cases."_ns);
|
||||
child->ReportNoTests();
|
||||
#endif // XP_UNIX
|
||||
}
|
||||
|
||||
@ -84,12 +84,29 @@ void RunTestsSocket(SandboxTestingChild* child) {
|
||||
# endif // XP_LINUX
|
||||
|
||||
#else // XP_UNIX
|
||||
child->SendReportTestResults(
|
||||
"dummy_test"_ns,
|
||||
/* shouldSucceed */ true,
|
||||
/* didSucceed */ true,
|
||||
"The test framework fails if there are no cases."_ns);
|
||||
child->ReportNoTests();
|
||||
#endif // XP_UNIX
|
||||
}
|
||||
|
||||
void RunTestsRDD(SandboxTestingChild* child) {
|
||||
MOZ_ASSERT(child, "No SandboxTestingChild*?");
|
||||
|
||||
#ifdef XP_UNIX
|
||||
# ifdef XP_LINUX
|
||||
child->ErrnoValueTest("ioctl_tiocsti"_ns, false, ENOSYS, [&] {
|
||||
int rv = ioctl(1, TIOCSTI, "x");
|
||||
return rv;
|
||||
});
|
||||
|
||||
struct rusage res;
|
||||
child->ErrnoTest("getrusage"_ns, true, [&] {
|
||||
int rv = getrusage(RUSAGE_SELF, &res);
|
||||
return rv;
|
||||
});
|
||||
# endif // XP_LINUX
|
||||
#else // XP_UNIX
|
||||
child->ReportNoTests();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -14,7 +14,7 @@ function test() {
|
||||
// GPU process might not run depending on the platform, so we need it to be
|
||||
// the last one of the list to allow the remainingTests logic below to work
|
||||
// as expected.
|
||||
var processTypes = ["tab", "socket", "gpu"];
|
||||
var processTypes = ["tab", "socket", "rdd", "gpu"];
|
||||
|
||||
// A callback called after each test-result.
|
||||
let sandboxTestResult = (subject, topic, data) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user