implement Milestone 14: location interface with coarse/fine precision and rate limiting

This commit is contained in:
2026-01-18 16:07:32 +01:00
parent c2e8b8c212
commit 779f66b2bb
6 changed files with 1454 additions and 1 deletions

View File

@@ -27,6 +27,7 @@ add_library(mosis-sandbox STATIC
../src/main/cpp/sandbox/camera_interface.cpp
../src/main/cpp/sandbox/microphone_interface.cpp
../src/main/cpp/sandbox/audio_output.cpp
../src/main/cpp/sandbox/location_interface.cpp
)
target_include_directories(mosis-sandbox PUBLIC
../src/main/cpp/sandbox

View File

@@ -17,6 +17,7 @@
#include "camera_interface.h"
#include "microphone_interface.h"
#include "audio_output.h"
#include "location_interface.h"
#include <filesystem>
#include <fstream>
#include <sstream>
@@ -2323,6 +2324,201 @@ bool Test_AudioLuaIntegration(std::string& error_msg) {
return true;
}
//=============================================================================
// MILESTONE 14: Location
//=============================================================================
bool Test_LocationRequiresPermission(std::string& error_msg) {
mosis::LocationInterface location("test.app");
// No permissions granted
mosis::LocationOptions options;
std::string err;
bool started = location.GetCurrentPosition(
options,
[](const mosis::Position&) {},
[](mosis::LocationError, const std::string&) {},
err
);
EXPECT_TRUE(!started);
EXPECT_TRUE(err.find("permission") != std::string::npos);
return true;
}
bool Test_LocationCoarseReducesPrecision(std::string& error_msg) {
mosis::LocationInterface location("test.app");
location.SetCoarsePermission(true); // Only coarse, not fine
// Set mock position with high precision
mosis::Position precise;
precise.latitude = 37.7749295;
precise.longitude = -122.4194155;
precise.accuracy = 5.0;
precise.altitude = 100.0;
location.SetMockPosition(precise);
bool got_position = false;
mosis::Position received;
mosis::LocationOptions options;
std::string err;
location.GetCurrentPosition(
options,
[&](const mosis::Position& pos) {
got_position = true;
received = pos;
},
[](mosis::LocationError, const std::string&) {},
err
);
EXPECT_TRUE(got_position);
// Should be rounded to ~0.01 degrees
EXPECT_TRUE(std::abs(received.latitude - 37.77) < 0.001);
EXPECT_TRUE(std::abs(received.longitude - (-122.42)) < 0.001);
// Accuracy should be at least 1000m
EXPECT_TRUE(received.accuracy >= 1000.0);
// Fine details should be zeroed
EXPECT_TRUE(received.altitude == 0.0);
return true;
}
bool Test_LocationRateLimits(std::string& error_msg) {
mosis::LocationInterface location("test.app");
location.SetCoarsePermission(true);
mosis::Position pos;
pos.latitude = 37.77;
pos.longitude = -122.42;
location.SetMockPosition(pos);
mosis::LocationOptions options;
// First request should succeed
std::string err;
bool ok1 = location.GetCurrentPosition(
options,
[](const mosis::Position&) {},
[](mosis::LocationError, const std::string&) {},
err
);
EXPECT_TRUE(ok1);
// Immediate second request should be rate limited
bool ok2 = location.GetCurrentPosition(
options,
[](const mosis::Position&) {},
[](mosis::LocationError, const std::string&) {},
err
);
EXPECT_TRUE(!ok2);
EXPECT_TRUE(err.find("rate") != std::string::npos ||
err.find("limit") != std::string::npos);
return true;
}
bool Test_LocationWatchPosition(std::string& error_msg) {
mosis::LocationInterface location("test.app");
location.SetFinePermission(true);
std::string err;
auto watch = location.WatchPosition({}, err);
EXPECT_TRUE(watch != nullptr);
EXPECT_TRUE(watch->IsActive());
EXPECT_TRUE(location.GetActiveWatchCount() == 1);
watch->Stop();
EXPECT_TRUE(!watch->IsActive());
return true;
}
bool Test_LocationWatchLimit(std::string& error_msg) {
mosis::LocationInterface location("test.app");
location.SetCoarsePermission(true);
std::vector<std::shared_ptr<mosis::LocationWatch>> watches;
std::string err;
// Create MAX_WATCHES (5) watches
for (int i = 0; i < 5; i++) {
auto watch = location.WatchPosition({}, err);
EXPECT_TRUE(watch != nullptr);
watches.push_back(watch);
}
// 6th should fail
auto extra = location.WatchPosition({}, err);
EXPECT_TRUE(extra == nullptr);
EXPECT_TRUE(err.find("limit") != std::string::npos ||
err.find("maximum") != std::string::npos);
return true;
}
bool Test_LocationCleansUpOnShutdown(std::string& error_msg) {
mosis::LocationInterface location("test.app");
location.SetCoarsePermission(true);
std::string err;
auto watch1 = location.WatchPosition({}, err);
auto watch2 = location.WatchPosition({}, err);
EXPECT_TRUE(location.GetActiveWatchCount() == 2);
location.Shutdown();
EXPECT_TRUE(location.GetActiveWatchCount() == 0);
EXPECT_TRUE(!watch1->IsActive());
EXPECT_TRUE(!watch2->IsActive());
return true;
}
bool Test_LocationLuaIntegration(std::string& error_msg) {
SandboxContext ctx = TestContext();
LuaSandbox sandbox(ctx);
mosis::LocationInterface location("test.app");
mosis::RegisterLocationAPI(sandbox.GetState(), &location);
std::string script = R"lua(
-- Test that location global exists
if not location then
error("location global not found")
end
if not location.getCurrentPosition then
error("location.getCurrentPosition not found")
end
if not location.watchPosition then
error("location.watchPosition not found")
end
if not location.clearWatch then
error("location.clearWatch not found")
end
if not location.getWatchCount then
error("location.getWatchCount not found")
end
-- Watch count should be 0 initially
if location.getWatchCount() ~= 0 then
error("should have no active watches initially")
end
)lua";
bool ok = sandbox.LoadString(script, "location_test");
if (!ok) {
error_msg = "Lua test failed: " + sandbox.GetLastError();
return false;
}
return true;
}
//=============================================================================
// MAIN
//=============================================================================
@@ -2488,6 +2684,15 @@ int main(int argc, char* argv[]) {
harness.AddTest("AudioStopsOnShutdown", Test_AudioStopsOnShutdown);
harness.AddTest("AudioLuaIntegration", Test_AudioLuaIntegration);
// Milestone 14: Location
harness.AddTest("LocationRequiresPermission", Test_LocationRequiresPermission);
harness.AddTest("LocationCoarseReducesPrecision", Test_LocationCoarseReducesPrecision);
harness.AddTest("LocationRateLimits", Test_LocationRateLimits);
harness.AddTest("LocationWatchPosition", Test_LocationWatchPosition);
harness.AddTest("LocationWatchLimit", Test_LocationWatchLimit);
harness.AddTest("LocationCleansUpOnShutdown", Test_LocationCleansUpOnShutdown);
harness.AddTest("LocationLuaIntegration", Test_LocationLuaIntegration);
// Run tests
auto results = harness.Run(filter);