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

« back to all changes in this revision

Viewing changes to tests/ichnaea_reporter_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:
 
1
/*
 
2
 * Copyright © 2012-2013 Canonical Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify it
 
5
 * under the terms of the GNU Lesser General Public License version 3,
 
6
 * as published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU Lesser General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored by: Thomas Voß <thomas.voss@canonical.com>
 
17
 */
 
18
 
 
19
#include <com/ubuntu/location/service/ichnaea_reporter.h>
 
20
 
 
21
#include "web_server.h"
 
22
 
 
23
#include <core/posix/fork.h>
 
24
#include <core/posix/signal.h>
 
25
#include <core/posix/wait.h>
 
26
#include <core/testing/cross_process_sync.h>
 
27
#include <core/testing/fork_and_run.h>
 
28
 
 
29
#include <json/json.h>
 
30
 
 
31
#include <gtest/gtest.h>
 
32
#include <gmock/gmock.h>
 
33
 
 
34
#include <condition_variable>
 
35
 
 
36
namespace json = Json;
 
37
namespace location = com::ubuntu::location;
 
38
 
 
39
namespace
 
40
{
 
41
struct MockWirelessNetwork : public location::connectivity::WirelessNetwork
 
42
{
 
43
    /** @brief Timestamp when the network became visible. */
 
44
    MOCK_CONST_METHOD0(last_seen, const core::Property<std::chrono::system_clock::time_point>&());
 
45
 
 
46
    /** @brief Returns the BSSID of the network */
 
47
    MOCK_CONST_METHOD0(bssid, const core::Property<std::string>&());
 
48
 
 
49
    /** @brief Returns the SSID of the network. */
 
50
    MOCK_CONST_METHOD0(ssid, const core::Property<std::string>&());
 
51
 
 
52
    /** @brief Returns the mode of the network. */
 
53
    MOCK_CONST_METHOD0(mode, const core::Property<Mode>&());
 
54
 
 
55
    /** @brief Returns the frequency that the network/AP operates upon. */
 
56
    MOCK_CONST_METHOD0(frequency, const core::Property<Frequency>&());
 
57
 
 
58
    /** @brief Returns the signal quality of the network/AP in percent. */
 
59
    MOCK_CONST_METHOD0(signal_strength, const core::Property<SignalStrength>&());
 
60
};
 
61
 
 
62
struct MockRadioCell : public location::connectivity::RadioCell
 
63
{
 
64
    typedef std::shared_ptr<MockRadioCell> Ptr;
 
65
 
 
66
    /** @brief Returns the type of the radio cell. */
 
67
    MOCK_CONST_METHOD0(changed, const core::Signal<>&());
 
68
 
 
69
    /** @brief Returns the type of the radio cell. */
 
70
    MOCK_CONST_METHOD0(type, Type());
 
71
 
 
72
    /** @brief Returns GSM-specific details or throws std::runtime_error if this is not a GSM radiocell. */
 
73
    MOCK_CONST_METHOD0(gsm, const Gsm&());
 
74
 
 
75
    /** @brief Returns UMTS-specific details or throws std::runtime_error if this is not a UMTS radiocell. */
 
76
    MOCK_CONST_METHOD0(umts, const Umts&());
 
77
 
 
78
    /** @brief Returns LTE-specific details or throws std::runtime_error if this is not an LTE radiocell. */
 
79
    MOCK_CONST_METHOD0(lte, const Lte&());
 
80
};
 
81
 
 
82
location::Update<location::Position> reference_position_update
 
83
{
 
84
    {
 
85
        location::wgs84::Latitude{9. * location::units::Degrees},
 
86
        location::wgs84::Longitude{53. * location::units::Degrees},
 
87
        location::wgs84::Altitude{-2. * location::units::Meters}
 
88
    },
 
89
    location::Clock::now()
 
90
};
 
91
 
 
92
}
 
93
 
 
94
TEST(IchnaeaReporter, issues_correct_posts_requests)
 
95
{
 
96
    using namespace ::testing;
 
97
 
 
98
    static const location::connectivity::RadioCell::Gsm gsm
 
99
    {
 
100
        location::connectivity::RadioCell::Gsm::MCC{42},
 
101
        location::connectivity::RadioCell::Gsm::MNC{42},
 
102
        location::connectivity::RadioCell::Gsm::LAC{42},
 
103
        location::connectivity::RadioCell::Gsm::ID{42},
 
104
        location::connectivity::RadioCell::Gsm::SignalStrength{21}
 
105
    };
 
106
 
 
107
    static const MockRadioCell::Ptr ref_cell
 
108
    {
 
109
        new NiceMock<MockRadioCell>()
 
110
    };
 
111
 
 
112
    ON_CALL(*ref_cell, type()).WillByDefault(Return(location::connectivity::RadioCell::Type::gsm));
 
113
    ON_CALL(*ref_cell, gsm()).WillByDefault(ReturnRef(gsm));
 
114
 
 
115
    static const core::Property<std::chrono::system_clock::time_point> ref_timestamp
 
116
    {
 
117
        std::chrono::system_clock::now()
 
118
    };
 
119
 
 
120
    static const core::Property<std::string> ref_bssid
 
121
    {
 
122
        "bssid:42"
 
123
    };
 
124
 
 
125
    static const core::Property<std::string> ref_ssid
 
126
    {
 
127
        "ssid:42"
 
128
    };
 
129
 
 
130
    static const core::Property<location::connectivity::WirelessNetwork::Mode> ref_mode
 
131
    {
 
132
        location::connectivity::WirelessNetwork::Mode::infrastructure
 
133
    };
 
134
 
 
135
    static const core::Property<location::connectivity::WirelessNetwork::Frequency> ref_frequency
 
136
    {
 
137
        location::connectivity::WirelessNetwork::Frequency{4242}
 
138
    };
 
139
 
 
140
    static const core::Property<location::connectivity::WirelessNetwork::SignalStrength> ref_strength
 
141
    {
 
142
        location::connectivity::WirelessNetwork::SignalStrength{80}
 
143
    };
 
144
 
 
145
    static const std::string api_key
 
146
    {
 
147
        "test_key"
 
148
    };
 
149
 
 
150
    static const std::string nick_name
 
151
    {
 
152
        "nick_name"
 
153
    };
 
154
 
 
155
    core::testing::CrossProcessSync cps; // server - ready -> client
 
156
 
 
157
    testing::web::server::Configuration web_server_configuration
 
158
    {
 
159
        5000,
 
160
        [](mg_connection* conn)
 
161
        {
 
162
            core::net::http::Header header;
 
163
 
 
164
            for (int i = 0; i < conn->num_headers; i++)
 
165
            {
 
166
                header.add(conn->http_headers[i].name, conn->http_headers[i].value);
 
167
            }
 
168
 
 
169
            EXPECT_TRUE(header.has(location::service::ichnaea::Reporter::nick_name_header));
 
170
            EXPECT_STREQ("/v1/submit", conn->uri);
 
171
            EXPECT_STREQ("POST", conn->request_method);
 
172
            EXPECT_EQ("key=" + api_key, conn->query_string);
 
173
            EXPECT_NE(nullptr, conn->content);
 
174
            EXPECT_GT(conn->content_len, 0);
 
175
 
 
176
            using namespace location::service::ichnaea;
 
177
 
 
178
            json::Reader reader;
 
179
            json::Value result;
 
180
 
 
181
            EXPECT_TRUE(reader.parse(conn->content, result));
 
182
 
 
183
            auto items = result[Reporter::Json::items];
 
184
            EXPECT_EQ(1u, items.size());
 
185
 
 
186
            auto item = items[0];
 
187
            EXPECT_EQ("gsm", item[Reporter::Json::radio].asString());
 
188
 
 
189
            EXPECT_DOUBLE_EQ(
 
190
                        reference_position_update.value.latitude.value.value(),
 
191
                        item[Reporter::Json::lat].asDouble());
 
192
            EXPECT_DOUBLE_EQ(
 
193
                        reference_position_update.value.longitude.value.value(),
 
194
                        item[Reporter::Json::lon].asDouble());
 
195
 
 
196
            auto wifis = item[Reporter::Json::wifi];
 
197
            EXPECT_EQ(1u, wifis.size());
 
198
 
 
199
            auto wifi = wifis[0];
 
200
            EXPECT_EQ(ref_bssid.get(), wifi[Reporter::Json::Wifi::key].asString());
 
201
            EXPECT_EQ(ref_frequency->get(), wifi[Reporter::Json::Wifi::frequency].asInt());
 
202
 
 
203
            auto cells = item[Reporter::Json::cell];
 
204
            EXPECT_EQ(1u, cells.size());
 
205
 
 
206
            auto cell = cells[0];
 
207
            EXPECT_EQ(ref_cell->gsm().mobile_country_code.get(), cell[Reporter::Json::Cell::mcc].asInt());
 
208
            EXPECT_EQ(ref_cell->gsm().mobile_network_code.get(), cell[Reporter::Json::Cell::mnc].asInt());
 
209
            EXPECT_EQ(ref_cell->gsm().location_area_code.get(), cell[Reporter::Json::Cell::lac].asInt());
 
210
            EXPECT_EQ(ref_cell->gsm().id.get(), cell[Reporter::Json::Cell::cid].asInt());
 
211
            EXPECT_EQ(ref_cell->gsm().strength.get(), cell[Reporter::Json::Cell::asu].asInt());
 
212
 
 
213
            mg_send_status(conn, static_cast<int>(submit::success));
 
214
            return MG_TRUE;
 
215
        }
 
216
    };
 
217
 
 
218
    core::posix::ChildProcess server = core::posix::fork(
 
219
                std::bind(testing::a_web_server(web_server_configuration), cps),
 
220
                core::posix::StandardStream::empty);
 
221
 
 
222
    using namespace ::testing;
 
223
 
 
224
    auto wireless_network = std::make_shared<NiceMock<MockWirelessNetwork> >();
 
225
    ON_CALL(*wireless_network, last_seen()).WillByDefault(ReturnRef(ref_timestamp));
 
226
    ON_CALL(*wireless_network, bssid()).WillByDefault(ReturnRef(ref_bssid));
 
227
    ON_CALL(*wireless_network, ssid()).WillByDefault(ReturnRef(ref_ssid));
 
228
    ON_CALL(*wireless_network, mode()).WillByDefault(ReturnRef(ref_mode));
 
229
    ON_CALL(*wireless_network, frequency()).WillByDefault(ReturnRef(ref_frequency));
 
230
    ON_CALL(*wireless_network, signal_strength()).WillByDefault(ReturnRef(ref_strength));
 
231
 
 
232
    location::service::ichnaea::Reporter::Configuration config
 
233
    {
 
234
        "http://127.0.0.1:5000",
 
235
        api_key,
 
236
        "nickname"
 
237
    };
 
238
 
 
239
    location::service::ichnaea::Reporter reporter{config};
 
240
 
 
241
    reporter.start();
 
242
    reporter.report(reference_position_update, {wireless_network}, {ref_cell});
 
243
 
 
244
    cps.wait_for_signal_ready_for(std::chrono::seconds{2});
 
245
 
 
246
    server.send_signal_or_throw(core::posix::Signal::sig_term);
 
247
    auto result = server.wait_for(core::posix::wait::Flags::untraced);
 
248
 
 
249
    EXPECT_EQ(core::posix::wait::Result::Status::exited, result.status);
 
250
    EXPECT_EQ(core::posix::exit::Status::success, result.detail.if_exited.status);
 
251
}