implement Milestone 12: Microphone interface with permission and user gesture requirements

This commit is contained in:
2026-01-18 15:45:30 +01:00
parent 5eb1113c1a
commit d61b8f0bd8
6 changed files with 1263 additions and 1 deletions

View File

@@ -25,6 +25,7 @@ add_library(mosis-sandbox STATIC
../src/main/cpp/sandbox/network_manager.cpp
../src/main/cpp/sandbox/websocket_manager.cpp
../src/main/cpp/sandbox/camera_interface.cpp
../src/main/cpp/sandbox/microphone_interface.cpp
)
target_include_directories(mosis-sandbox PUBLIC
../src/main/cpp/sandbox

View File

@@ -15,6 +15,7 @@
#include "network_manager.h"
#include "websocket_manager.h"
#include "camera_interface.h"
#include "microphone_interface.h"
#include <filesystem>
#include <fstream>
#include <sstream>
@@ -2007,6 +2008,173 @@ bool Test_CameraLuaIntegration(std::string& error_msg) {
return true;
}
//=============================================================================
// MILESTONE 12: Microphone
//=============================================================================
bool Test_MicrophoneRequiresPermission(std::string& error_msg) {
// Create sandbox context WITHOUT microphone permission
SandboxContext ctx;
ctx.app_id = "test.app";
ctx.permissions = {}; // No microphone permission declared
ctx.is_system_app = false;
PermissionGate permissions(ctx);
mosis::MicrophoneInterface mic("test.app", &permissions);
mic.SimulateUserGesture();
std::string err;
mosis::RecordingConfig config;
auto session = mic.StartSession(config, err);
EXPECT_TRUE(session == nullptr);
EXPECT_TRUE(err.find("permission") != std::string::npos);
return true;
}
bool Test_MicrophoneRequiresUserGesture(std::string& error_msg) {
// Create sandbox context WITH microphone permission
SandboxContext ctx;
ctx.app_id = "test.app";
ctx.permissions = {"microphone"};
ctx.is_system_app = false;
PermissionGate permissions(ctx);
permissions.GrantPermission("microphone");
mosis::MicrophoneInterface mic("test.app", &permissions);
// Note: NOT calling SimulateUserGesture()
std::string err;
mosis::RecordingConfig config;
auto session = mic.StartSession(config, err);
EXPECT_TRUE(session == nullptr);
EXPECT_TRUE(err.find("gesture") != std::string::npos);
return true;
}
bool Test_MicrophoneShowsIndicator(std::string& error_msg) {
SandboxContext ctx;
ctx.app_id = "test.app";
ctx.permissions = {"microphone"};
ctx.is_system_app = false;
PermissionGate permissions(ctx);
permissions.GrantPermission("microphone");
mosis::MicrophoneInterface mic("test.app", &permissions);
mic.SimulateUserGesture();
EXPECT_FALSE(mic.IsIndicatorVisible());
std::string err;
mosis::RecordingConfig config;
auto session = mic.StartSession(config, err);
EXPECT_TRUE(session != nullptr);
EXPECT_TRUE(mic.IsIndicatorVisible());
mic.StopSession();
EXPECT_FALSE(mic.IsIndicatorVisible());
return true;
}
bool Test_MicrophoneSingleSession(std::string& error_msg) {
SandboxContext ctx;
ctx.app_id = "test.app";
ctx.permissions = {"microphone"};
ctx.is_system_app = false;
PermissionGate permissions(ctx);
permissions.GrantPermission("microphone");
mosis::MicrophoneInterface mic("test.app", &permissions);
mic.SimulateUserGesture();
std::string err;
mosis::RecordingConfig config;
// First session should succeed
auto session1 = mic.StartSession(config, err);
EXPECT_TRUE(session1 != nullptr);
// Second session should fail
mic.SimulateUserGesture();
auto session2 = mic.StartSession(config, err);
EXPECT_TRUE(session2 == nullptr);
EXPECT_TRUE(err.find("active") != std::string::npos ||
err.find("already") != std::string::npos);
return true;
}
bool Test_MicrophoneStopsOnShutdown(std::string& error_msg) {
SandboxContext ctx;
ctx.app_id = "test.app";
ctx.permissions = {"microphone"};
ctx.is_system_app = false;
PermissionGate permissions(ctx);
permissions.GrantPermission("microphone");
mosis::MicrophoneInterface mic("test.app", &permissions);
mic.SimulateUserGesture();
std::string err;
mosis::RecordingConfig config;
auto session = mic.StartSession(config, err);
EXPECT_TRUE(session != nullptr);
EXPECT_TRUE(mic.HasActiveSession());
// Simulate app stop
mic.Shutdown();
EXPECT_FALSE(mic.HasActiveSession());
EXPECT_FALSE(mic.IsIndicatorVisible());
return true;
}
bool Test_MicrophoneLuaIntegration(std::string& error_msg) {
SandboxContext ctx = TestContext();
ctx.permissions = {"microphone"};
LuaSandbox sandbox(ctx);
PermissionGate permissions(ctx);
permissions.GrantPermission("microphone");
mosis::MicrophoneInterface mic("test.app", &permissions);
mic.SimulateUserGesture();
mosis::RegisterMicrophoneAPI(sandbox.GetState(), &mic);
std::string script = R"lua(
-- Test that microphone global exists
if not microphone then
error("microphone global not found")
end
if not microphone.start then
error("microphone.start not found")
end
if not microphone.isActive then
error("microphone.isActive not found")
end
-- isActive should be false initially
if microphone.isActive() then
error("microphone should not be active initially")
end
)lua";
bool ok = sandbox.LoadString(script, "microphone_test");
if (!ok) {
error_msg = "Lua test failed: " + sandbox.GetLastError();
return false;
}
return true;
}
//=============================================================================
// MAIN
//=============================================================================
@@ -2156,6 +2324,14 @@ int main(int argc, char* argv[]) {
harness.AddTest("CameraStopsOnShutdown", Test_CameraStopsOnShutdown);
harness.AddTest("CameraLuaIntegration", Test_CameraLuaIntegration);
// Milestone 12: Microphone
harness.AddTest("MicrophoneRequiresPermission", Test_MicrophoneRequiresPermission);
harness.AddTest("MicrophoneRequiresUserGesture", Test_MicrophoneRequiresUserGesture);
harness.AddTest("MicrophoneShowsIndicator", Test_MicrophoneShowsIndicator);
harness.AddTest("MicrophoneSingleSession", Test_MicrophoneSingleSession);
harness.AddTest("MicrophoneStopsOnShutdown", Test_MicrophoneStopsOnShutdown);
harness.AddTest("MicrophoneLuaIntegration", Test_MicrophoneLuaIntegration);
// Run tests
auto results = harness.Run(filter);