~thomas-voss/location-service/fix-1347887

« back to all changes in this revision

Viewing changes to tests/gps_provider_test.cpp

This MP consolidates multiple related changes together, with the goal of:

(1.) Make the service instance accessible via a cli. Useful for testing scenarios.
(2.) To cut down time-to-first-fix (ttff) by:
  (2.1) Leveraging SUPL and other supplementary data downloaded over ordinary data connections.
  (2.2) Enabling network-based positioning providers to acquire fast position estimates.

In more detail:

* Added tests for daemon and cli.
* Unified daemon and cli header and implementation files.
* Add a command-line interface to the service.
* Split up provider selection policy to rely on an interface ProviderEnumerator to ease in testing.
* Trimmed down on types.
* Removed connectivity API draft to prepare for simpler approach.
* Refactored includes.
* Added a configuration option to handle cell and wifi ID reporting.
* Add a mock for a connectivity API exposed to providers and reporters.
* Add units for connectivity api.
* Refactor cell class into namespace radio. Fixes: 1226204, 1248973, 1281817

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
 *
16
16
 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17
17
 */
18
 
#include "com/ubuntu/location/providers/gps.h"
 
18
#include <com/ubuntu/location/providers/gps/provider.h>
 
19
#include <com/ubuntu/location/providers/gps/android_hardware_abstraction_layer.h>
 
20
 
 
21
//TODO(tvoss) Reenable
 
22
//#include <com/ubuntu/location/providers/gps/net_cpp_gps_xtra_downloader.h>
 
23
 
 
24
#include <com/ubuntu/location/logging.h>
 
25
#include <com/ubuntu/location/service/program_options.h>
 
26
 
 
27
#include <core/posix/fork.h>
 
28
#include <core/posix/this_process.h>
 
29
 
 
30
#include <core/testing/cross_process_sync.h>
19
31
 
20
32
#include <gmock/gmock.h>
21
33
#include <gtest/gtest.h>
22
34
 
23
 
TEST(GeoclueProvider, accessing_starting_and_stopping_gps_provider_works)
24
 
{
25
 
    com::ubuntu::location::GpsProvider provider;
 
35
#include <boost/accumulators/accumulators.hpp>
 
36
#include <boost/accumulators/statistics/stats.hpp>
 
37
#include <boost/accumulators/statistics/mean.hpp>
 
38
#include <boost/accumulators/statistics/variance.hpp>
 
39
 
 
40
#include <chrono>
 
41
#include <condition_variable>
 
42
#include <mutex>
 
43
#include <thread>
 
44
 
 
45
#include "web_server.h"
 
46
 
 
47
namespace gps = com::ubuntu::location::providers::gps;
 
48
namespace location = com::ubuntu::location;
 
49
 
 
50
namespace
 
51
{
 
52
struct UpdateTrap
 
53
{
 
54
    MOCK_METHOD1(on_position_updated, void(const location::Position&));
 
55
    MOCK_METHOD1(on_heading_updated, void(const location::Heading&));
 
56
    MOCK_METHOD1(on_velocity_updated, void(const location::Velocity&));
 
57
    MOCK_METHOD1(on_space_vehicles_updated, void(const std::set<location::SpaceVehicle>&));
 
58
};
 
59
 
 
60
struct MockSuplAssistant : public gps::HardwareAbstractionLayer::SuplAssistant
 
61
{
 
62
    MockSuplAssistant()
 
63
    {
 
64
        using namespace ::testing;
 
65
 
 
66
        ON_CALL(*this, status()).WillByDefault(ReturnRef(status_));
 
67
        ON_CALL(*this, server_ip()).WillByDefault(ReturnRef(ip_address_));
 
68
    }
 
69
 
 
70
    MOCK_CONST_METHOD0(status, const core::Property<Status>&());
 
71
    MOCK_CONST_METHOD0(server_ip, const core::Property<IpV4Address>&());
 
72
    MOCK_METHOD2(set_server, void(const std::string&, std::uint16_t));
 
73
    MOCK_METHOD1(notify_data_connection_open_via_apn, void(const std::string&));
 
74
    MOCK_METHOD0(notify_data_connection_closed, void());
 
75
    MOCK_METHOD0(notify_data_connection_not_available, void());
 
76
 
 
77
    core::Property<Status> status_;
 
78
    core::Property<IpV4Address> ip_address_;
 
79
};
 
80
 
 
81
struct MockHardwareAbstractionLayer : public gps::HardwareAbstractionLayer
 
82
{
 
83
    MockHardwareAbstractionLayer()
 
84
    {
 
85
        using namespace ::testing;
 
86
 
 
87
        ON_CALL(*this, supl_assistant()).WillByDefault(ReturnRef(supl_assistant_));
 
88
        ON_CALL(*this, start_positioning()).WillByDefault(Return(true));
 
89
        ON_CALL(*this, stop_positioning()).WillByDefault(Return(true));
 
90
        ON_CALL(*this, position_updates()).WillByDefault(ReturnRef(position_updates_));
 
91
        ON_CALL(*this, heading_updates()).WillByDefault(ReturnRef(heading_updates_));
 
92
        ON_CALL(*this, velocity_updates()).WillByDefault(ReturnRef(velocity_updates_));
 
93
        ON_CALL(*this, space_vehicle_updates()).WillByDefault(ReturnRef(space_vehicle_updates_));
 
94
        ON_CALL(*this, chipset_status()).WillByDefault(ReturnRef(chipset_status_));
 
95
    }
 
96
 
 
97
    MOCK_METHOD0(supl_assistant, gps::HardwareAbstractionLayer::SuplAssistant&());
 
98
    MOCK_CONST_METHOD0(position_updates, const core::Signal<location::Position>& ());
 
99
    MOCK_CONST_METHOD0(heading_updates, const core::Signal<location::Heading>&());
 
100
    MOCK_CONST_METHOD0(velocity_updates, const core::Signal<location::Velocity>& ());
 
101
    MOCK_CONST_METHOD0(space_vehicle_updates, const core::Signal<std::set<location::SpaceVehicle>>&());
 
102
    MOCK_METHOD0(delete_all_aiding_data, void());
 
103
    MOCK_CONST_METHOD0(chipset_status, const core::Property<gps::ChipsetStatus>&());
 
104
    MOCK_CONST_METHOD1(is_capable_of, bool(gps::AssistanceMode));
 
105
    MOCK_CONST_METHOD1(is_capable_of, bool(gps::PositionMode));
 
106
    MOCK_CONST_METHOD1(is_capable_of, bool(gps::Capability capability));
 
107
    MOCK_METHOD0(start_positioning, bool());
 
108
    MOCK_METHOD0(stop_positioning, bool());
 
109
    MOCK_METHOD1(set_assistance_mode, bool(gps::AssistanceMode));
 
110
    MOCK_METHOD1(set_position_mode, bool(gps::PositionMode));
 
111
    MOCK_METHOD1(inject_reference_position, bool(const location::Position&));
 
112
    MOCK_METHOD2(inject_reference_time,
 
113
                 bool(const std::chrono::microseconds&,
 
114
                      const std::chrono::microseconds&));
 
115
 
 
116
    MockSuplAssistant supl_assistant_;
 
117
    core::Signal<location::Position> position_updates_;
 
118
    core::Signal<location::Heading> heading_updates_;
 
119
    core::Signal<location::Velocity> velocity_updates_;
 
120
    core::Signal<std::set<location::SpaceVehicle>> space_vehicle_updates_;
 
121
    core::Property<gps::ChipsetStatus> chipset_status_;
 
122
};
 
123
 
 
124
auto gps_conf =
 
125
R"foo(
 
126
# XTRA_SERVER_QUERY (1=on, 0=off)
 
127
# If XTRA_SERVER_QUERY is on, the XTRA_SERVERs listed
 
128
# below will be ignored, and instead the servers will
 
129
# be queried from the modem.
 
130
XTRA_SERVER_QUERY=0
 
131
# XTRA_SERVERs below are used only if XTRA_SERVER_QUERY
 
132
# is off.
 
133
XTRA_SERVER_1=http://xtra1.gpsonextra.net/xtra2.bin
 
134
XTRA_SERVER_2=http://xtra2.gpsonextra.net/xtra2.bin
 
135
XTRA_SERVER_3=http://xtra3.gpsonextra.net/xtra2.bin
 
136
 
 
137
# Error Estimate
 
138
# _SET = 1
 
139
# _CLEAR = 0
 
140
ERR_ESTIMATE=0
 
141
 
 
142
#Test
 
143
NTP_SERVER=time.gpsonextra.net
 
144
#Asia
 
145
# NTP_SERVER=asia.pool.ntp.org
 
146
#Europe
 
147
# NTP_SERVER=europe.pool.ntp.org
 
148
#North America
 
149
# NTP_SERVER=north-america.pool.ntp.org
 
150
 
 
151
# DEBUG LEVELS: 0 - none, 1 - Error, 2 - Warning, 3 - Info
 
152
#               4 - Debug, 5 - Verbose
 
153
# If DEBUG_LEVEL is commented, Android's logging levels will be used
 
154
DEBUG_LEVEL = 2
 
155
 
 
156
# Intermediate position report, 1=enable, 0=disable
 
157
INTERMEDIATE_POS=0
 
158
 
 
159
# supl version 1.0
 
160
SUPL_VER=0x10000
 
161
 
 
162
# GPS Capabilities bit mask
 
163
# SCHEDULING = 0x01
 
164
# MSB = 0x02
 
165
# MSA = 0x04
 
166
# ON_DEMAND_TIME = 0x10
 
167
# GEOFENCE = 0x20
 
168
# default = ON_DEMAND_TIME | MSA | MSB | SCHEDULING | GEOFENCE
 
169
CAPABILITIES=0x33
 
170
 
 
171
# Accuracy threshold for intermediate positions
 
172
# less accurate positions are ignored, 0 for passing all positions
 
173
# ACCURACY_THRES=5000
 
174
 
 
175
################################
 
176
##### AGPS server settings #####
 
177
################################
 
178
 
 
179
# FOR SUPL SUPPORT, set the following
 
180
# SUPL_HOST=supl.host.com or IP
 
181
# SUPL_PORT=1234
 
182
SUPL_HOST=supl.google.com
 
183
SUPL_PORT=7275
 
184
 
 
185
# FOR C2K PDE SUPPORT, set the following
 
186
# C2K_HOST=c2k.pde.com or IP
 
187
# C2K_PORT=1234
 
188
 
 
189
####################################
 
190
#  LTE Positioning Profile Settings
 
191
####################################
 
192
# 0: Enable RRLP on LTE(Default)
 
193
# 1: Enable LPP_User_Plane on LTE
 
194
# 2: Enable LPP_Control_Plane
 
195
# 3: Enable both LPP_User_Plane and LPP_Control_Plane
 
196
LPP_PROFILE = 0
 
197
 
 
198
################################
 
199
# EXTRA SETTINGS
 
200
################################
 
201
# NMEA provider (1=Modem Processor, 0=Application Processor)
 
202
NMEA_PROVIDER=0
 
203
 
 
204
##################################################
 
205
# Select Positioning Protocol on A-GLONASS system
 
206
##################################################
 
207
# 0x1: RRC CPlane
 
208
# 0x2: RRLP UPlane
 
209
# 0x4: LLP Uplane
 
210
A_GLONASS_POS_PROTOCOL_SELECT = 0
 
211
)foo";
 
212
 
 
213
}
 
214
 
 
215
TEST(AndroidGpsXtraDownloader, reading_configuration_from_valid_conf_file_works)
 
216
{
 
217
    std::stringstream ss{gps_conf};
 
218
 
 
219
    auto config = gps::android::GpsXtraDownloader::Configuration::from_gps_conf_ini_file(ss);
 
220
 
 
221
    EXPECT_EQ(3, config.xtra_hosts.size());
 
222
 
 
223
    EXPECT_EQ("http://xtra1.gpsonextra.net/xtra2.bin", config.xtra_hosts.at(0));
 
224
    EXPECT_EQ("http://xtra2.gpsonextra.net/xtra2.bin", config.xtra_hosts.at(1));
 
225
    EXPECT_EQ("http://xtra3.gpsonextra.net/xtra2.bin", config.xtra_hosts.at(2));
 
226
}
 
227
 
 
228
TEST(GpsProvider, starting_updates_on_a_provider_instance_calls_into_the_hal)
 
229
{
 
230
    using namespace ::testing;
 
231
 
 
232
    NiceMock<MockHardwareAbstractionLayer> hal;
 
233
    std::shared_ptr<gps::HardwareAbstractionLayer> hal_ptr(&hal, [](gps::HardwareAbstractionLayer*){});
 
234
 
 
235
    gps::Provider provider(hal_ptr);
 
236
 
 
237
    EXPECT_CALL(hal, start_positioning()).Times(3);
 
238
    // Stop positioning will be called by the provider's dtor.
 
239
    // Thus 3 explicit stops and 1 implicit.
 
240
    EXPECT_CALL(hal, stop_positioning()).Times(4);
 
241
 
 
242
    provider.start_position_updates();
 
243
    provider.start_heading_updates();
 
244
    provider.start_velocity_updates();
 
245
 
 
246
    provider.stop_position_updates();
 
247
    provider.stop_heading_updates();
 
248
    provider.stop_velocity_updates();
 
249
}
 
250
 
 
251
TEST(GpsProvider, injecting_a_reference_position_calls_into_the_hal)
 
252
{
 
253
    using namespace ::testing;
 
254
 
 
255
    NiceMock<MockHardwareAbstractionLayer> hal;
 
256
    std::shared_ptr<gps::HardwareAbstractionLayer> hal_ptr(&hal, [](gps::HardwareAbstractionLayer*){});
 
257
 
 
258
    gps::Provider provider(hal_ptr);
 
259
    location::Position pos;
 
260
    EXPECT_CALL(hal, inject_reference_position(pos)).Times(1);
 
261
 
 
262
    provider.on_reference_location_updated(pos);
 
263
}
 
264
 
 
265
TEST(GpsProvider, updates_from_hal_are_passed_on_by_the_provider)
 
266
{
 
267
    using namespace ::testing;
 
268
 
 
269
    NiceMock<MockHardwareAbstractionLayer> hal;
 
270
    std::shared_ptr<gps::HardwareAbstractionLayer> hal_ptr(&hal, [](gps::HardwareAbstractionLayer*){});
 
271
 
 
272
    UpdateTrap update_trap;
 
273
 
 
274
    gps::Provider provider(hal_ptr);
 
275
    location::Position pos;
 
276
    location::Heading heading;
 
277
    location::Velocity velocity;
 
278
    std::set<location::SpaceVehicle> svs;
 
279
 
 
280
    provider.updates().position.connect([&update_trap](const location::Update<location::Position>& pos)
 
281
    {
 
282
        update_trap.on_position_updated(pos.value);
 
283
    });
 
284
    provider.updates().heading.connect([&update_trap](const location::Update<location::Heading>& heading)
 
285
    {
 
286
        update_trap.on_heading_updated(heading.value);
 
287
    });
 
288
    provider.updates().velocity.connect([&update_trap](const location::Update<location::Velocity>& velocity)
 
289
    {
 
290
        update_trap.on_velocity_updated(velocity.value);
 
291
    });
 
292
    provider.updates().svs.connect([&update_trap](const location::Update<std::set<location::SpaceVehicle>>& svs)
 
293
    {
 
294
        update_trap.on_space_vehicles_updated(svs.value);
 
295
    });
 
296
 
 
297
    EXPECT_CALL(update_trap, on_position_updated(pos)).Times(1);
 
298
    EXPECT_CALL(update_trap, on_heading_updated(heading)).Times(1);
 
299
    EXPECT_CALL(update_trap, on_velocity_updated(velocity)).Times(1);
 
300
    EXPECT_CALL(update_trap, on_space_vehicles_updated(svs)).Times(1);
 
301
 
 
302
    hal.position_updates_(pos);
 
303
    hal.heading_updates_(heading);
 
304
    hal.velocity_updates_(velocity);
 
305
    hal.space_vehicle_updates_(svs);
 
306
}
 
307
 
 
308
// TODO(tvoss): Reenable
 
309
/*
 
310
TEST(GpsXtraDownloader, throws_for_missing_xtra_hosts)
 
311
{
 
312
    gps::android::NetCppGpsXtraDownloader downloader;
 
313
    EXPECT_ANY_THROW(downloader.download_xtra_data(gps::android::GpsXtraDownloader::Configuration{}));
 
314
}
 
315
 
 
316
TEST(GpsXtraDownloader, downloading_xtra_data_from_known_host_works)
 
317
{
 
318
    static const int data_size{200};
 
319
 
 
320
    core::testing::CrossProcessSync cps; // server - ready -> client
 
321
 
 
322
    testing::web::server::Configuration configuration
 
323
    {
 
324
        5000,
 
325
        [&cps](mg_connection* conn)
 
326
        {
 
327
            static char data[data_size];
 
328
            for (int i = 0; i < data_size; i++)
 
329
                data[i] = i%2;
 
330
 
 
331
            std::map<std::string, std::set<std::string>> header;
 
332
 
 
333
            for (int i = 0; i < conn->num_headers; i++)
 
334
            {
 
335
                header[conn->http_headers[i].name].insert(conn->http_headers[i].value);
 
336
            }
 
337
 
 
338
            EXPECT_TRUE(header.at("ACCEPT").count("") == 1);
 
339
            EXPECT_TRUE(header.at("ACCEPT").count("application/vnd.wap.mms-message") == 1);
 
340
            EXPECT_TRUE(header.at("ACCEPT").count("application/vnd.wap.sic") == 1);
 
341
            EXPECT_TRUE(header.at("X-WAP-PROFILE")
 
342
                        .count(gps::android::GpsXtraDownloader::x_wap_profile_value) == 1);
 
343
 
 
344
            mg_send_status(conn, 200);
 
345
            mg_send_data(conn, &data, data_size);
 
346
 
 
347
            return MG_TRUE;
 
348
        }
 
349
    };
 
350
 
 
351
    auto server = core::posix::fork(
 
352
                std::bind(testing::a_web_server(configuration), cps),
 
353
                core::posix::StandardStream::empty);
 
354
 
 
355
    cps.wait_for_signal_ready_for(std::chrono::seconds{2});
 
356
 
 
357
    gps::android::GpsXtraDownloader::Configuration config;
 
358
    config.xtra_hosts.push_back("http://127.0.0.1:5000");
 
359
 
 
360
    gps::android::NetCppGpsXtraDownloader downloader;
 
361
    auto result = downloader.download_xtra_data(config);
 
362
 
 
363
    EXPECT_EQ(data_size, result.size());
 
364
    for (int i = 0; i < data_size; i++)
 
365
        EXPECT_EQ(i%2, result[i]);
 
366
}
 
367
 
 
368
TEST(GpsXtraDownloader, throws_for_unreachable_host)
 
369
{
 
370
    gps::android::GpsXtraDownloader::Configuration config;
 
371
    config.xtra_hosts.push_back("http://does_not_exist.host.com/");
 
372
 
 
373
    gps::android::NetCppGpsXtraDownloader downloader;
 
374
    EXPECT_ANY_THROW(downloader.download_xtra_data(config));
 
375
}
 
376
 
 
377
TEST(GpsXtraDownloader, download_attempt_throws_if_timeout_is_reached)
 
378
{
 
379
    testing::web::server::Configuration web_serverconfiguration
 
380
    {
 
381
        5000,
 
382
        [](mg_connection*)
 
383
        {
 
384
            return MG_TRUE;
 
385
        }
 
386
    };
 
387
 
 
388
    core::testing::CrossProcessSync cps; // server - ready -> client
 
389
 
 
390
    auto server = core::posix::fork(
 
391
                std::bind(testing::a_web_server(web_serverconfiguration), cps),
 
392
                core::posix::StandardStream::empty);
 
393
 
 
394
    cps.wait_for_signal_ready_for(std::chrono::seconds{2});
 
395
 
 
396
    gps::android::GpsXtraDownloader::Configuration download_config;
 
397
    download_config.xtra_hosts.push_back("http://127.0.0.1:5000");
 
398
 
 
399
    gps::android::NetCppGpsXtraDownloader downloader;
 
400
    EXPECT_ANY_THROW(downloader.download_xtra_data(download_config));
 
401
}
 
402
*/
 
403
 
 
404
/*****************************************************************
 
405
 *                                                               *
 
406
 * All tests requiring hardware go here. They are named with     *
 
407
 * the suffix requires_hardware to indicate that they shouldn't  *
 
408
 * be executed as part of the usual build/test cycle. Instead    *
 
409
 * they are packaged up for later execution on an emulated or    *
 
410
 * real device.                                                  *
 
411
 *                                                               *
 
412
 ****************************************************************/
 
413
TEST(GpsProvider, DISABLED_accessing_starting_and_stopping_gps_provider_works_requires_hardware)
 
414
{
 
415
    com::ubuntu::location::providers::gps::Provider provider;
26
416
    EXPECT_NO_THROW(provider.start_position_updates());
27
417
    EXPECT_NO_THROW(provider.stop_position_updates());
28
418
    EXPECT_NO_THROW(provider.start_velocity_updates());
30
420
    EXPECT_NO_THROW(provider.start_heading_updates());
31
421
    EXPECT_NO_THROW(provider.stop_heading_updates());
32
422
}
 
423
 
 
424
// We are carrying out quite some positioning here and leverage that fact for feeding location
 
425
// and wifi/cell data to Mozilla location service instances. Please note that we feed to the mozilla location service
 
426
// in the general case.
 
427
#include <com/ubuntu/location/service/harvester.h>
 
428
 
 
429
// TODO(tvoss): Enable reporting to the Mozilla location service.
 
430
// #include <com/ubuntu/location/service/ichnaea_reporter.h>
 
431
 
 
432
namespace
 
433
{
 
434
struct NullReporter : public location::service::Harvester::Reporter
 
435
{
 
436
    NullReporter() = default;
 
437
 
 
438
    /** @brief Tell the reporter that it should start operating. */
 
439
    void start()
 
440
    {
 
441
    }
 
442
 
 
443
    /** @brief Tell the reporter to shut down its operation. */
 
444
    void stop()
 
445
    {
 
446
    }
 
447
 
 
448
    /**
 
449
     * @brief Triggers the reporter to send off the information.
 
450
     */
 
451
    void report(const location::Update<location::Position>&,
 
452
                const std::vector<location::connectivity::WirelessNetwork::Ptr>&,
 
453
                const std::vector<location::connectivity::RadioCell::Ptr>&)
 
454
    {
 
455
    }
 
456
};
 
457
 
 
458
struct HardwareAbstractionLayerFixture : public ::testing::Test
 
459
{
 
460
    // If this key is set to any value in the environment, we send off data to Mozilla location
 
461
    // service instances. ENABLE_HARVESTING_DURING_TESTS
 
462
    static constexpr const char* enable_harvesting{"enable_harvesting_during_tests"};
 
463
    // The host name of the Mozilla location service instance.
 
464
    static constexpr const char* ichnaea_host{"ichnaea_host"};
 
465
    // The API key to submit under.
 
466
    static constexpr const char* ichnaea_api_key{"ichnaea_api_key"};
 
467
    // The API key to submit under.
 
468
    static constexpr const char* ichnaea_nickname{"ichnaea_nickname"};
 
469
    // Reference latitude value.
 
470
    static constexpr const char* ref_lat{"ref_lat"};
 
471
    // Reference longitude value.
 
472
    static constexpr const char* ref_lon{"ref_lon"};
 
473
    // Reference horizontal accuracy value.
 
474
    static constexpr const char* ref_accuracy{"ref_accuracy"};
 
475
    // SUPL host address
 
476
    static constexpr const char* supl_host{"supl_host"};
 
477
    // SUPL port
 
478
    static constexpr const char* supl_port{"supl_port"};
 
479
 
 
480
    static location::ProgramOptions init_options()
 
481
    {
 
482
        location::ProgramOptions options;
 
483
 
 
484
        options.environment_prefix("GPS_TEST_");
 
485
 
 
486
        options.add(enable_harvesting,
 
487
                    "If this key is set to any value in the environment, "
 
488
                    "we send off data to Mozilla location service instances.",
 
489
                    false);
 
490
 
 
491
        options.add(ichnaea_host,
 
492
                    "The host name of the Mozilla location service instance.",
 
493
                    std::string{"https://location.services.mozilla.com"});
 
494
 
 
495
        options.add(ichnaea_api_key,
 
496
                    "The API key to submit under.",
 
497
                    std::string{"ubuntu_location_service_test_cases"});
 
498
 
 
499
        options.add(ichnaea_nickname,
 
500
                    "The nickname to submit under.",
 
501
                    std::string{"ubuntu_location_service"});
 
502
 
 
503
        options.add(ref_lat,
 
504
                    "Reference latitude value.",
 
505
                    double{51.444670});
 
506
 
 
507
        options.add(ref_lon,
 
508
                    "Reference longitude value.",
 
509
                    double{7.210852});
 
510
 
 
511
        options.add(ref_accuracy,
 
512
                    "Reference horizontal accuracy value.",
 
513
                    double{10});
 
514
 
 
515
        options.add(supl_host,
 
516
                    "SUPL host to use for positioning benchmarks.",
 
517
                    std::string{"supl.google.com"});
 
518
 
 
519
        options.add(supl_port,
 
520
                    "SUPL port to use for positioning benchmarks.",
 
521
                    std::uint16_t{7476});
 
522
 
 
523
        return options;
 
524
    }
 
525
 
 
526
    HardwareAbstractionLayerFixture()
 
527
    {
 
528
        options.print(LOG(INFO));
 
529
        harvester.start();
 
530
    }
 
531
 
 
532
    void SetUp()
 
533
    {
 
534
        // We need to make sure that we are running as root. In addition, we will stop
 
535
        // any running location service instance prior to executing the test.
 
536
        if (!(::getuid() == 0))
 
537
            FAIL() << "This test has to be run as root.";
 
538
 
 
539
        int rc = ::system("service ubuntu-location-service stop");
 
540
 
 
541
        // We consciously ignore the return code of the command here.
 
542
        // The location service might not have been running before and with that
 
543
        // the command would return an error, although the precondition has been successfully
 
544
        // established.
 
545
        if (rc < 0)
 
546
            FAIL() << "Unable to stop the location service as part of the test setup.";
 
547
    }
 
548
 
 
549
    void TearDown()
 
550
    {
 
551
        int rc = ::system("service ubuntu-location-service start");
 
552
        (void) rc;
 
553
    }
 
554
 
 
555
    bool IsHarvestingDuringTestsEnabled() const
 
556
    {
 
557
        return options.value_for_key<bool>(enable_harvesting);
 
558
    }
 
559
 
 
560
    location::ProgramOptions options = init_options();
 
561
    bool options_parsed_from_env
 
562
    {
 
563
        options.parse_from_environment()
 
564
    };
 
565
    // The Ichnaea instance and its configuration.
 
566
    //location::service::ichnaea::Reporter::Configuration reporter_configuration
 
567
    //{
 
568
    //    options.value_for_key<std::string>(ichnaea_host),
 
569
    //    options.value_for_key<std::string>(ichnaea_api_key),
 
570
    //    options.value_for_key<std::string>(ichnaea_nickname)
 
571
    //};
 
572
    //std::shared_ptr<location::service::ichnaea::Reporter> reporter
 
573
    //{
 
574
    //    new location::service::ichnaea::Reporter{reporter_configuration}
 
575
    //};
 
576
    // The harvester instance and its configuration.
 
577
    location::service::Harvester::Configuration harvester_configuration
 
578
    {
 
579
        location::connectivity::platform_default_manager(),
 
580
        location::service::Harvester::Reporter::Ptr{new NullReporter{}}
 
581
    };
 
582
    location::service::Harvester harvester
 
583
    {
 
584
        harvester_configuration
 
585
    };
 
586
    // Reference position for SUPL benchmarks.
 
587
    location::Position ref_pos
 
588
    {
 
589
        location::wgs84::Latitude
 
590
        {
 
591
            options.value_for_key<double>(ref_lat) * location::units::Degrees
 
592
        },
 
593
        location::wgs84::Longitude
 
594
        {
 
595
            options.value_for_key<double>(ref_lon) * location::units::Degrees
 
596
        },
 
597
        location::wgs84::Altitude
 
598
        {
 
599
            0 * location::units::Meter
 
600
        },
 
601
        location::units::Quantity<location::units::Length>
 
602
        {
 
603
            options.value_for_key<double>(ref_accuracy) * location::units::Meters
 
604
        }
 
605
    };
 
606
};
 
607
}
 
608
 
 
609
TEST_F(HardwareAbstractionLayerFixture, DISABLED_provider_construction_works_requires_hardware)
 
610
{
 
611
    {
 
612
        location::ProviderFactory::instance().create_provider_for_name_with_config("gps::Provider", location::Configuration{});
 
613
    }
 
614
 
 
615
    {
 
616
        gps::Provider provider;
 
617
    }
 
618
 
 
619
    {
 
620
        gps::Provider::create_instance(location::Configuration{});
 
621
    }
 
622
 
 
623
    {
 
624
        location::ProviderFactory::instance().create_provider_for_name_with_config("gps::Provider", location::Configuration{});
 
625
    }
 
626
}
 
627
 
 
628
// HardwareAbstractionLayerFixture.time_to_first_fix_cold_start_without_supl_benchmark_requires_hardware
 
629
TEST_F(HardwareAbstractionLayerFixture, time_to_first_fix_cold_start_without_supl_benchmark_requires_hardware)
 
630
{
 
631
    typedef boost::accumulators::accumulator_set<
 
632
        double,
 
633
        boost::accumulators::stats<
 
634
            boost::accumulators::tag::mean,
 
635
            boost::accumulators::tag::variance
 
636
        >
 
637
    > Statistics;
 
638
 
 
639
    using boost::accumulators::mean;
 
640
    using boost::accumulators::variance;
 
641
 
 
642
    static const unsigned int trials = 3;
 
643
 
 
644
    Statistics stats;
 
645
    auto hal = gps::HardwareAbstractionLayer::create_default_instance();
 
646
 
 
647
    struct State
 
648
    {
 
649
        State() : fix_received(false)
 
650
        {
 
651
        }
 
652
 
 
653
        bool wait_for_fix_for(const std::chrono::seconds& seconds)
 
654
        {
 
655
            std::unique_lock<std::mutex> ul(guard);
 
656
            return wait_condition.wait_for(
 
657
                        ul,
 
658
                        seconds,
 
659
                        [this]() {return fix_received == true;});
 
660
        }
 
661
 
 
662
        void on_position_updated(const location::Position&)
 
663
        {
 
664
            fix_received = true;
 
665
            wait_condition.notify_all();
 
666
        }
 
667
 
 
668
        void reset()
 
669
        {
 
670
            fix_received = false;
 
671
        }
 
672
 
 
673
        std::mutex guard;
 
674
        std::condition_variable wait_condition;
 
675
        bool fix_received;
 
676
    } state;
 
677
 
 
678
    // We want to run in standalone mode
 
679
    hal->set_assistance_mode(gps::AssistanceMode::standalone);
 
680
 
 
681
    // We wire up our state to position updates from the hal.
 
682
    hal->position_updates().connect([this, &state](const location::Position& pos)
 
683
    {
 
684
        if (IsHarvestingDuringTestsEnabled())
 
685
            harvester.report_position_update(location::Update<location::Position>
 
686
            {
 
687
                pos, location::Clock::now()
 
688
            });
 
689
        state.on_position_updated(pos);
 
690
    });
 
691
 
 
692
    for (unsigned int i = 0; i < trials; i++)
 
693
    {
 
694
        std::cout << "Executing trial " << i << " of " << trials << " trials" << std::endl;
 
695
        // We want to force a cold start per trial.
 
696
        hal->delete_all_aiding_data();
 
697
 
 
698
        state.reset();
 
699
        auto start = std::chrono::duration_cast<std::chrono::microseconds>(location::Clock::now().time_since_epoch());
 
700
        {
 
701
            hal->start_positioning();
 
702
            // We expect a maximum cold start time of 15 minutes. The theoretical
 
703
            // limit is 12.5 minutes, and we add up some grace period to make the
 
704
            // test more robust (see http://en.wikipedia.org/wiki/Time_to_first_fix).
 
705
            EXPECT_TRUE(state.wait_for_fix_for(std::chrono::seconds{15 * 60}));
 
706
            hal->stop_positioning();
 
707
        }
 
708
        auto stop = std::chrono::duration_cast<std::chrono::microseconds>(location::Clock::now().time_since_epoch());
 
709
 
 
710
        stats((stop - start).count());
 
711
    }
 
712
 
 
713
    std::cout << "Mean time to first fix in [ms]: "
 
714
              << std::chrono::duration_cast<std::chrono::milliseconds>(
 
715
                     std::chrono::microseconds(
 
716
                         static_cast<std::uint64_t>(mean(stats)))).count()
 
717
              << std::endl;
 
718
    std::cout << "Variance in time to first fix in [ms]: "
 
719
              << std::chrono::duration_cast<std::chrono::milliseconds>(
 
720
                     std::chrono::microseconds(
 
721
                         static_cast<std::uint64_t>(variance(stats)))).count()
 
722
              << std::endl;
 
723
}
 
724
 
 
725
// HardwareAbstractionLayerFixture.time_to_first_fix_cold_start_with_supl_benchmark_requires_hardware
 
726
TEST_F(HardwareAbstractionLayerFixture, time_to_first_fix_cold_start_with_supl_benchmark_requires_hardware)
 
727
{
 
728
    using namespace core::posix;
 
729
 
 
730
    typedef boost::accumulators::accumulator_set<
 
731
        double,
 
732
        boost::accumulators::stats<
 
733
            boost::accumulators::tag::mean,
 
734
            boost::accumulators::tag::variance
 
735
        >
 
736
    > Statistics;
 
737
 
 
738
    using boost::accumulators::mean;
 
739
    using boost::accumulators::variance;
 
740
 
 
741
    static const unsigned int trials = 3;
 
742
 
 
743
    Statistics stats;
 
744
    auto hal = gps::HardwareAbstractionLayer::create_default_instance();
 
745
 
 
746
    struct State
 
747
    {
 
748
        State() : fix_received(false)
 
749
        {
 
750
        }
 
751
 
 
752
        bool wait_for_fix_for(const std::chrono::seconds& seconds)
 
753
        {
 
754
            std::unique_lock<std::mutex> ul(guard);
 
755
            return wait_condition.wait_for(
 
756
                        ul,
 
757
                        seconds,
 
758
                        [this]() {return fix_received == true;});
 
759
        }
 
760
 
 
761
        void on_position_updated(const location::Position&)
 
762
        {
 
763
            fix_received = true;
 
764
            wait_condition.notify_all();
 
765
        }
 
766
 
 
767
        void reset()
 
768
        {
 
769
            fix_received = false;
 
770
        }
 
771
 
 
772
        std::mutex guard;
 
773
        std::condition_variable wait_condition;
 
774
        bool fix_received;
 
775
    } state;
 
776
 
 
777
    // We wire up our state to position updates from the hal.
 
778
    hal->position_updates().connect([this, &state](const location::Position& pos)
 
779
    {
 
780
        if (IsHarvestingDuringTestsEnabled())
 
781
            harvester.report_position_update(location::Update<location::Position>
 
782
            {
 
783
                pos, location::Clock::now()
 
784
            });
 
785
        state.on_position_updated(pos);
 
786
    });
 
787
 
 
788
    for (unsigned int i = 0; i < trials; i++)
 
789
    {
 
790
        std::cout << "Executing trial " << i << " of " << trials << " trials" << std::endl;
 
791
 
 
792
        // We want to force a cold start per trial.
 
793
        hal->delete_all_aiding_data();
 
794
        state.reset();
 
795
 
 
796
        // We want to run in assisted mode
 
797
        EXPECT_TRUE(hal->set_assistance_mode(gps::AssistanceMode::mobile_station_based));
 
798
 
 
799
        auto supl_host = options.value_for_key<std::string>(HardwareAbstractionLayerFixture::supl_host);
 
800
        auto supl_port = options.value_for_key<std::uint16_t>(HardwareAbstractionLayerFixture::supl_port);
 
801
 
 
802
        hal->supl_assistant().set_server(supl_host, supl_port);
 
803
 
 
804
        auto start = std::chrono::duration_cast<std::chrono::microseconds>(location::Clock::now().time_since_epoch());
 
805
        {
 
806
            bool running = true;
 
807
 
 
808
            hal->start_positioning();
 
809
 
 
810
            std::thread injector([this, hal, &running]()
 
811
            {
 
812
                while (running)
 
813
                {
 
814
                    hal->inject_reference_position(ref_pos);
 
815
                    std::this_thread::sleep_for(std::chrono::seconds{1});
 
816
                }
 
817
            });
 
818
 
 
819
            // We expect a maximum cold start time of 15 minutes. The theoretical
 
820
            // limit is 12.5 minutes, and we add up some grace period to make the
 
821
            // test more robust (see http://en.wikipedia.org/wiki/Time_to_first_fix).
 
822
            EXPECT_TRUE(state.wait_for_fix_for(std::chrono::seconds{15 * 60}));
 
823
            hal->stop_positioning();
 
824
 
 
825
            running = false;
 
826
            if (injector.joinable())
 
827
                injector.join();
 
828
 
 
829
        }
 
830
        auto stop = std::chrono::duration_cast<std::chrono::microseconds>(location::Clock::now().time_since_epoch());
 
831
 
 
832
        stats((stop - start).count());
 
833
    }
 
834
 
 
835
    std::cout << "Mean time to first fix in [ms]: "
 
836
              << std::chrono::duration_cast<std::chrono::milliseconds>(
 
837
                     std::chrono::microseconds(
 
838
                         static_cast<std::uint64_t>(mean(stats)))).count()
 
839
              << std::endl;
 
840
    std::cout << "Variance in time to first fix in [ms]: "
 
841
              << std::chrono::duration_cast<std::chrono::milliseconds>(
 
842
                     std::chrono::microseconds(
 
843
                         static_cast<std::uint64_t>(variance(stats)))).count()
 
844
              << std::endl;
 
845
}
 
846