Merge lp:~pitti/platform-api/test-backend into lp:platform-api

Proposed by Martin Pitt
Status: Merged
Approved by: Ricardo Mendoza
Approved revision: 207
Merged at revision: 182
Proposed branch: lp:~pitti/platform-api/test-backend
Merge into: lp:platform-api
Diff against target: 1137 lines (+988/-11)
13 files modified
CMakeLists.txt (+6/-0)
debian/control (+18/-0)
debian/libubuntu-application-api-test1.docs (+1/-0)
debian/libubuntu-application-api-test1.install (+1/-0)
debian/rules (+6/-0)
src/ubuntu/CMakeLists.txt (+2/-1)
src/ubuntu/hybris/CMakeLists.txt (+1/-0)
src/ubuntu/hybris/bridge.h (+29/-10)
src/ubuntu/testbackend/CMakeLists.txt (+21/-0)
src/ubuntu/testbackend/README.md (+77/-0)
src/ubuntu/testbackend/ubuntu_application_sensors.cpp (+553/-0)
tests/CMakeLists.txt (+30/-0)
tests/test_ubuntu_application_sensors.cpp (+243/-0)
To merge this branch: bzr merge lp:~pitti/platform-api/test-backend
Reviewer Review Type Date Requested Status
Ricardo Mendoza (community) Approve
PS Jenkins bot continuous-integration Approve
Thomas Voß (community) Approve
Review via email: mp+198098@code.launchpad.net

Commit message

Add backend for simulated sensor data.

Description of the change

This provides an initial implementation of simulated sensors, as discussed in
http://pad.ubuntu.com/sensors-testing.

You can now run an app with the simulated sensor backend
(UBUNTU_PLATFORM_API_BACKEND=libubuntu_application_test_api.so) and point it to
a simulation data file (UBUNTU_PLATFORM_API_SENSOR_TEST=/path/to/test.sensor)
and get these events delivered.

This also introduces a test suite and tests for the simulation sensor backend.

Note that this will currently fail, we need to land
https://code.launchpad.net/~thomas-voss/process-cpp/fix_pkgconfig_setup/+merge/198025
and then add a versioned build dep. This also adopts the rather higher-level black magic TEST_FP macro, so that this can go away.

This is an initial MP for reviewing the general structure, my code style (I'm a
C++ novice, so please tell me what to improve), the test structure, etc.

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

Note that this just provides the initial building blocks. Once that lands, we most probably want to add some more commands to the test data files, such as a command to create a set of standard sensors, or composite events like "turn device by 90 degrees" (either by new commands, or by providing a library of test data files, remains to be discussed).

I'll also create some tests in qtubuntu-sensors which use the QtSensors API, to ensure that the data is correctly passed back and forth through QtSensors/qtubuntu-sensors/platform-api.

Revision history for this message
Martin Pitt (pitti) wrote :

Note to self: Need to package/ship new backend and README.

Revision history for this message
Thomas Voß (thomas-voss) wrote :

Looks great, thanks for getting the testing effort started :)

A few minor niggles:

l. 300ff: You can switch "class" to "struct" and get rid of the explicit "public" specifiers. No need to make it a class if default visibility is public.

l. 341ff: Instead of returning a pointer (and managing its lifetime), you could as well return a mutable reference and initialize it as in:

static SensorController& instance()
{
    static SensorController inst_;
    return inst_;
}

With that, the instance is created exactly once on first access, and released after main automatically.

In line 381, you could then make the type->sensor instance mapping non-static, and switch to a shared_ptr for handling the lifetime of the pointer: map<ubuntu_sensor_type, std::shared_ptr<TestSensor>> sensors.

For stylistic karma: The functions handling time could be switched over to use std::chrono (see http://en.cppreference.com/w/cpp/chrono/duration). With that, you don't need comments in the source code specifying the timebase but just return std::chrono::microseconds or std::chrono::nanoseconds. Casting between time bases happens automatically then. With std::chrono, the current time can be sampled with std::chrono::high_resolution_clock::now() or std::chrono::steady_clock::now().

review: Needs Fixing
Revision history for this message
Martin Pitt (pitti) wrote :

Did the changes Thomas suggested in his review, thanks!

Revision history for this message
Martin Pitt (pitti) wrote :

More code review appreciated, but so far I'm happy with this as a first version. Plan:

- Wait for process-cpp to land, then bump build-dep on it in this MP
- Get this MP landed
- Add some high-level integration tests to qtubuntu-sensors which use the QtSensors API, to make sure that qtsensors-opensource-src → qtubuntusensors → platform-api communication all works (not blocked by the above, can be developed in parallel)
- land these tests

After that we should meet with some app developers to discuss what kind of more abstract events they would like to get, and add them to our sensor test API (the file parser).

Revision history for this message
Martin Pitt (pitti) wrote :

process-cpp landed at last, so I made the necessary build-dep changes and dropped TEST_FP which now moved into process-cpp. Ready now from my POV.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~pitti/platform-api/test-backend updated
203. By Martin Pitt

Declare functions which return float as "aapcs" calling convention on ARM, like the hybris library

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~pitti/platform-api/test-backend updated
204. By Martin Pitt

avoid integer overflow in itimerspec calculation, and fix precision warnings

Revision history for this message
Thomas Voß (thomas-voss) wrote :

LGTM, again: Thanks for getting this started.

review: Approve
Revision history for this message
Martin Pitt (pitti) wrote :

Ricardo, can you please do another review, and top-approve if you are happy with it? Thanks!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Martin Pitt (pitti) wrote :

Notes from IRC from Rick: It should stop building a *.so symlink for the test backend, and libubuntu-application-test-api1 should be libubuntu-application-api-test1 to follow naming for mirserver/mirclient/hybris backends.

Another thing to look at: the test .so only provides the sensor functions, not the whole thing; the bridge could be more clever about that and load the other symbols from the real backend; but that's supposedly not blocking the MP.

lp:~pitti/platform-api/test-backend updated
205. By Martin Pitt

merge with trunk

206. By Martin Pitt

Don't install a *.so symlink for libubuntu_application_test_api

This is not a development library to link against, and users (i. e. test
suites) should always specify the ABI version.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ricardo Mendoza (ricmm) wrote :

As said by pitti the missing providers for non-sensor related helpers is not a blocker for this MR, as this lays the groundwork for the general testing and implements only the sensor backend.

A followup MR will split backends and make more smart use of the bridge to dynamically load each component area.

Currently just missing the package/SO rename and it should be good to go.

review: Needs Fixing
lp:~pitti/platform-api/test-backend updated
207. By Martin Pitt

rename libubuntu-application-test-api1 to libubuntu-application-api-test1

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ricardo Mendoza (ricmm) wrote :

Looks good, tested across devices. All comments addressed.

Landing.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2013-08-22 06:32:14 +0000
+++ CMakeLists.txt 2014-01-08 15:17:50 +0000
@@ -93,3 +93,9 @@
93add_subdirectory(doc/)93add_subdirectory(doc/)
94add_subdirectory(include/)94add_subdirectory(include/)
95add_subdirectory(src/)95add_subdirectory(src/)
96
97#### Enable tests
98include(CTest)
99set(GTEST_ROOT /usr/src/gtest)
100add_subdirectory(tests/)
101add_subdirectory(${GTEST_ROOT} ${CMAKE_CURRENT_BINARY_DIR}/gtest)
96102
=== modified file 'debian/control'
--- debian/control 2014-01-02 21:25:57 +0000
+++ debian/control 2014-01-08 15:17:50 +0000
@@ -17,6 +17,8 @@
17 libdbus-1-dev,17 libdbus-1-dev,
18 dbus-cpp-dev,18 dbus-cpp-dev,
19 libubuntu-location-service-dev,19 libubuntu-location-service-dev,
20 libgtest-dev,
21 libprocess-cpp-dev (>= 0.0.1+14.04.20131212),
20Standards-Version: 3.9.422Standards-Version: 3.9.4
21Homepage: https://launchpad.net/platform-api23Homepage: https://launchpad.net/platform-api
22Vcs-Bzr: lp:platform-api24Vcs-Bzr: lp:platform-api
@@ -142,6 +144,22 @@
142 The produced library should be used via libhybris, to communicate with the144 The produced library should be used via libhybris, to communicate with the
143 Android userspace, which is where the Ubuntu Application Manager lives.145 Android userspace, which is where the Ubuntu Application Manager lives.
144146
147Package: libubuntu-application-api-test1
148Section: libs
149Architecture: any
150Pre-Depends: ${misc:Pre-Depends},
151Multi-Arch: same
152Depends: ${misc:Depends},
153 ${shlibs:Depends},
154Description: test implementation of the Platform API
155 This package provides an implementation of the Platform API for testing. It
156 does not communicate with actual hardware, but reads input from a text file.
157 .
158 You need to explicitly enable this at runtime. Please see the README for
159 details.
160 .
161 For now this provides simulated sensors.
162
145Package: libplatform-api1-hybris-tests163Package: libplatform-api1-hybris-tests
146Architecture: any164Architecture: any
147Depends: ${misc:Depends},165Depends: ${misc:Depends},
148166
=== added file 'debian/libubuntu-application-api-test1.docs'
--- debian/libubuntu-application-api-test1.docs 1970-01-01 00:00:00 +0000
+++ debian/libubuntu-application-api-test1.docs 2014-01-08 15:17:50 +0000
@@ -0,0 +1,1 @@
1src/ubuntu/testbackend/README.md
02
=== added file 'debian/libubuntu-application-api-test1.install'
--- debian/libubuntu-application-api-test1.install 1970-01-01 00:00:00 +0000
+++ debian/libubuntu-application-api-test1.install 2014-01-08 15:17:50 +0000
@@ -0,0 +1,1 @@
1usr/lib/*/libubuntu_application_api_test.so.*
02
=== modified file 'debian/rules'
--- debian/rules 2013-08-14 12:16:09 +0000
+++ debian/rules 2014-01-08 15:17:50 +0000
@@ -13,3 +13,9 @@
1313
14overrid_dh_install:14overrid_dh_install:
15 dh_install --fail-missing15 dh_install --fail-missing
16
17override_dh_auto_test:
18ifeq (, $(findstring nocheck, $(DEB_BUILD_OPTIONS)))
19 cd obj-* && ctest --verbose
20endif
21
1622
=== modified file 'src/ubuntu/CMakeLists.txt'
--- src/ubuntu/CMakeLists.txt 2013-08-23 13:48:59 +0000
+++ src/ubuntu/CMakeLists.txt 2014-01-08 15:17:50 +0000
@@ -3,6 +3,7 @@
3)3)
44
5add_subdirectory(application)5add_subdirectory(application)
6add_subdirectory(testbackend)
67
7set(8set(
8 UBUNTU_APPLICATION_API_LINK_LIBRARIES9 UBUNTU_APPLICATION_API_LINK_LIBRARIES
@@ -25,4 +26,4 @@
2526
26if(ENABLE_MIRCLIENT_IMPLEMENTATION)27if(ENABLE_MIRCLIENT_IMPLEMENTATION)
27 add_subdirectory(mirclient/)28 add_subdirectory(mirclient/)
28endif()
29\ No newline at end of file29\ No newline at end of file
30endif()
3031
=== modified file 'src/ubuntu/hybris/CMakeLists.txt'
--- src/ubuntu/hybris/CMakeLists.txt 2013-08-17 05:22:31 +0000
+++ src/ubuntu/hybris/CMakeLists.txt 2014-01-08 15:17:50 +0000
@@ -27,6 +27,7 @@
2727
28target_link_libraries(28target_link_libraries(
29 ubuntu_platform_hardware_api29 ubuntu_platform_hardware_api
30 dl
30 hybris-common31 hybris-common
31)32)
3233
3334
=== modified file 'src/ubuntu/hybris/bridge.h'
--- src/ubuntu/hybris/bridge.h 2013-12-03 07:27:23 +0000
+++ src/ubuntu/hybris/bridge.h 2014-01-08 15:17:50 +0000
@@ -22,6 +22,8 @@
22#include <assert.h>22#include <assert.h>
23#include <dlfcn.h>23#include <dlfcn.h>
24#include <stddef.h>24#include <stddef.h>
25#include <stdlib.h>
26#include <string.h>
2527
26#define HIDDEN_SYMBOL __attribute__ ((visibility ("hidden")))28#define HIDDEN_SYMBOL __attribute__ ((visibility ("hidden")))
2729
@@ -39,19 +41,24 @@
39namespace internal41namespace internal
40{42{
4143
44/* By default we load the backend from /system/lib/libubuntu_application_api.so
45 * Programs can select a different backend with $UBUNTU_PLATFORM_API_BACKEND,
46 * which either needs to be a full path or just the file name (then it will be
47 * looked up in the usual library search path, see dlopen(3)).
48 */
42struct HIDDEN_SYMBOL ToApplication49struct HIDDEN_SYMBOL ToApplication
43{50{
44 static const char* path()51 static const char* path()
45 {52 {
46 return "/system/lib/libubuntu_application_api.so";53 static const char* cache = NULL;
47 }54
48};55 if (cache == NULL) {
4956 cache = secure_getenv("UBUNTU_PLATFORM_API_BACKEND");
50struct HIDDEN_SYMBOL ToHardware57 if (cache == NULL)
51{58 cache = "/system/lib/libubuntu_application_api.so";
52 static const char* path()59 }
53 {60
54 return "/system/lib/libubuntu_platform_hardware_api.so";61 return cache;
55 }62 }
56};63};
5764
@@ -67,12 +74,23 @@
6774
68 void* resolve_symbol(const char* symbol) const75 void* resolve_symbol(const char* symbol) const
69 {76 {
70 return android_dlsym(lib_handle, symbol);77 return dlsym_fn(lib_handle, symbol);
71 }78 }
7279
73 protected:80 protected:
74 Bridge() : lib_handle(android_dlopen(Scope::path(), RTLD_LAZY))81 Bridge() : lib_handle(android_dlopen(Scope::path(), RTLD_LAZY))
75 {82 {
83 const char* path = Scope::path();
84 /* use Android dl functions for Android libs in /system/, glibc dl
85 * functions for others */
86 if (strncmp(path, "/system/", 8) == 0) {
87 lib_handle = android_dlopen(path, RTLD_LAZY);
88 dlsym_fn = android_dlsym;
89 } else {
90 lib_handle = dlopen(path, RTLD_LAZY);
91 dlsym_fn = dlsym;
92 }
93
76 assert(lib_handle && "Error loading ubuntu_application_api");94 assert(lib_handle && "Error loading ubuntu_application_api");
77 }95 }
7896
@@ -82,6 +100,7 @@
82 }100 }
83101
84 void* lib_handle;102 void* lib_handle;
103 void* (*dlsym_fn) (void*, const char*);
85};104};
86105
87}106}
88107
=== added directory 'src/ubuntu/testbackend'
=== added file 'src/ubuntu/testbackend/CMakeLists.txt'
--- src/ubuntu/testbackend/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/ubuntu/testbackend/CMakeLists.txt 2014-01-08 15:17:50 +0000
@@ -0,0 +1,21 @@
1set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11")
2
3add_library(
4 ubuntu_application_api_test SHARED
5 ubuntu_application_sensors.cpp
6)
7
8set_target_properties(
9 ubuntu_application_api_test
10 PROPERTIES
11 VERSION ${UBUNTU_PLATFORM_API_VERSION_MAJOR}.${UBUNTU_PLATFORM_API_VERSION_MINOR}.${UBUNTU_PLATFORM_API_VERSION_PATCH}
12 SOVERSION ${UBUNTU_PLATFORM_API_VERSION_MAJOR}
13)
14
15install(
16 TARGETS ubuntu_application_api_test
17 # this is not a development library to compile against, users should always
18 # specify the SONAME; so don't build a *.so
19 LIBRARY DESTINATION "${LIB_INSTALL_DIR}" NAMELINK_SKIP
20)
21
022
=== added file 'src/ubuntu/testbackend/README.md'
--- src/ubuntu/testbackend/README.md 1970-01-01 00:00:00 +0000
+++ src/ubuntu/testbackend/README.md 2014-01-08 15:17:50 +0000
@@ -0,0 +1,77 @@
1Testing applications with simulated sensor data
2===============================================
3
4Purpose
5-------
6platform-api's interface to sensor data is in the shared library
7`libubuntu_platform_hardware_api.so`. That is only a stub which dynamically
8(dlopen) loads a backend library which provides the actual implementation. By
9default this is `/system/lib/libubuntu_application_api.so` which reads sensor
10data from the Android side. For testing purposes this can be replaced with this
11`libubuntu_application_api_test.so.1` which simulates sensors and their data based
12on a simple text input file.
13
14Using the test sensors
15----------------------
16Run your application under test with the environment variable
17
18 UBUNTU_PLATFORM_API_BACKEND=libubuntu_application_api_test.so.1
19
20and make sure that ld.so(8) can find it. If you don't have the library
21installed in a standard system library path, it is recommended to set
22`LD_LIBRARY_PATH` to the directory that contains the library (usually when using
23the library right out of the build tree). Alternatively you can specify the
24full path in `$UBUNTU_PLATFORM_API_BACKEND`.
25
26The env variable `$UBUNTU_PLATFORM_API_SENSOR_TEST` needs to point to a file that
27describes the desired sensor behaviour.
28
29Data format
30-----------
31The test sensors use a simple line based file format. The first part
32instantiates desired sensors with their parameters:
33
34 create [accel|light] <min> <max> <resolution>
35 # but no arguments for proximity sensor:
36 create proximity
37
38After that, it defines events; <delay> specifies time after previous event
39in ms:
40
41 <delay> proximity [unknown|near|far]
42 <delay> light <value>
43 <delay> accel <x> <y> <z>
44
45Empty lines and comment lines (starting with #) are allowed.
46
47Example file:
48
49 create light 0 10 1
50 create accel 0 1000 0.1
51 create proximity
52
53 200 proximity near
54 500 light 5
55 # simulate crash on the ground
56 500 accel 0.2 0.1 10
57 100 accel 0.2 0.2 1000
58 20 accel 0 0 0
59 10 proximity far
60 0 light 10
61
62
63Complete example
64----------------
65 * Build platform-api:
66
67 mkdir obj; (cd obj; cmake .. && make)
68
69 * Put above example file into /tmp/test.sensors
70
71 * Run the sensor test with it:
72
73 LD_LIBRARY_PATH=obj/src/ubuntu/testbackend \
74 UBUNTU_PLATFORM_API_BACKEND=libubuntu_application_api_test.so.1 \
75 UBUNTU_PLATFORM_API_SENSOR_TEST=/tmp/test.sensors \
76 obj/src/ubuntu/hybris/tests/test_android_sensors_api
77
078
=== added file 'src/ubuntu/testbackend/ubuntu_application_sensors.cpp'
--- src/ubuntu/testbackend/ubuntu_application_sensors.cpp 1970-01-01 00:00:00 +0000
+++ src/ubuntu/testbackend/ubuntu_application_sensors.cpp 2014-01-08 15:17:50 +0000
@@ -0,0 +1,553 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 3 as
6 * 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: Martin Pitt <martin.pitti@ubuntu.com>
17 */
18
19#include <ubuntu/application/sensors/ubuntu_application_sensors.h>
20#include <ubuntu/application/sensors/accelerometer.h>
21#include <ubuntu/application/sensors/proximity.h>
22#include <ubuntu/application/sensors/light.h>
23
24#include <cstddef>
25#include <cstdlib>
26#include <cstring>
27#include <cerrno>
28#include <csignal>
29#include <iostream>
30#include <sstream>
31#include <fstream>
32#include <stdexcept>
33#include <chrono>
34#include <map>
35#include <memory>
36
37using namespace std;
38
39// necessary for functions that return float
40// pcs attribute (calling convention) is only defined on ARM, avoid warning on
41// other platforms
42#ifdef __arm__
43#define __SF_FN_ATTR __attribute__((pcs("aapcs")))
44#else
45#define __SF_FN_ATTR
46#endif
47
48
49/***************************************
50 *
51 * test sensor implementation
52 *
53 ***************************************/
54
55// this is only internal API, so we make everything public
56struct TestSensor
57{
58 TestSensor(ubuntu_sensor_type _type, float _min_value, float _max_value, float _resolution) :
59 type(_type),
60 enabled(false),
61 resolution(_resolution),
62 min_delay(0),
63 min_value(_min_value),
64 max_value(_max_value),
65 on_event_cb(NULL),
66 event_cb_context(NULL),
67 x(_min_value),
68 y(_min_value),
69 z(_min_value),
70 distance((UASProximityDistance) 0), // LP#1256969
71 timestamp(0)
72 {}
73
74 ubuntu_sensor_type type;
75 bool enabled;
76 float resolution;
77 uint32_t min_delay;
78 float min_value, max_value;
79 void (*on_event_cb)(void*, void*);
80 void* event_cb_context;
81
82 /* current value; note that we do not track separate Event objects/pointers
83 * at all, and just always deliver the current value */
84 float x, y, z;
85 UASProximityDistance distance;
86 uint64_t timestamp;
87};
88
89/* Singleton which reads the sensor data file and maintains the TestSensor
90 * instances */
91class SensorController
92{
93 public:
94 // Ensure that controller is initialized, and return singleton
95 static SensorController& instance()
96 {
97 static SensorController _inst;
98 return _inst;
99 }
100
101 // Return TestSensor of given type, or NULL if it doesn't exist
102 TestSensor* get(ubuntu_sensor_type type)
103 {
104 try {
105 return sensors.at(type).get();
106 } catch (const out_of_range&) {
107 return NULL;
108 }
109 }
110
111 private:
112 SensorController();
113 bool next_command();
114 bool process_create_command();
115 void process_event_command();
116 void setup_timer(unsigned delay_ms);
117 static void on_timer(union sigval sval);
118
119 static ubuntu_sensor_type type_from_name(const string& type)
120 {
121 if (type == "light")
122 return ubuntu_sensor_type_light;
123 if (type == "proximity")
124 return ubuntu_sensor_type_proximity;
125 if (type == "accel")
126 return ubuntu_sensor_type_accelerometer;
127
128 cerr << "TestSensor ERROR: unknown sensor type " << type << endl;
129 abort();
130 }
131
132 map<ubuntu_sensor_type, shared_ptr<TestSensor>> sensors;
133 ifstream data;
134
135 // current command/event
136 string current_command;
137 TestSensor* event_sensor;
138 float event_x, event_y, event_z;
139 UASProximityDistance event_distance;
140};
141
142SensorController::SensorController()
143{
144 const char* path = getenv("UBUNTU_PLATFORM_API_SENSOR_TEST");
145
146 if (path == NULL) {
147 cerr << "TestSensor ERROR: Need $UBUNTU_PLATFORM_API_SENSOR_TEST to point to a data file\n";
148 abort();
149 }
150
151 //cout << "SensorController ctor: opening " << path << endl;
152
153 data.open(path);
154 if (!data.is_open()) {
155 cerr << "TestSensor ERROR: Failed to open data file " << path << ": " << strerror(errno) << endl;
156 abort();
157 }
158
159 // process all "create" commands
160 while (next_command()) {
161 if (!process_create_command())
162 break;
163 }
164
165 // start event processing
166 if (!data.eof())
167 process_event_command();
168}
169
170bool
171SensorController::next_command()
172{
173 while (getline(data, current_command)) {
174 // trim leading and trailing space
175 current_command.erase(0, current_command.find_first_not_of(" \t"));
176 current_command.erase(current_command.find_last_not_of(" \t") + 1);
177 // ignore empty or comment lines
178 if (current_command.size() == 0 || current_command[0] == '#')
179 continue;
180 return true;
181 }
182 return false;
183}
184
185bool
186SensorController::process_create_command()
187{
188 stringstream ss(current_command, ios_base::in);
189 string token;
190
191 // we only process "create" commands here; if we have something else, stop
192 ss >> token;
193 if (token != "create")
194 return false;
195
196 ss >> token;
197 ubuntu_sensor_type type = type_from_name(token);
198
199 if (get(type) != NULL) {
200 cerr << "TestSensor ERROR: duplicate creation of sensor type " << token << endl;
201 abort();
202 }
203
204 float min = 0, max = 0, resolution = 0;
205
206 if (type != ubuntu_sensor_type_proximity) {
207 // read min, max, resolution
208 ss >> min >> max >> resolution;
209
210 if (max <= min) {
211 cerr << "TestSensor ERROR: max_value must be >= min_value in " << current_command << endl;
212 abort();
213 }
214 if (resolution <= 0) {
215 cerr << "TestSensor ERROR: resolution must be > 0 in " << current_command << endl;
216 abort();
217 }
218 }
219
220 //cout << "SensorController::process_create_command: type " << type << " min " << min << " max " << max << " res " << resolution << endl;
221
222 sensors[type] = make_shared<TestSensor>(type, min, max, resolution);
223 return true;
224}
225
226void
227SensorController::process_event_command()
228{
229 stringstream ss(current_command, ios_base::in);
230 int delay;
231
232 //cout << "TestSensor: processing event " << current_command << endl;
233
234 // parse delay
235 ss >> delay;
236 if (delay <= 0) {
237 cerr << "TestSensor ERROR: delay must be positive in command " << current_command << endl;
238 abort();
239 }
240
241 // parse sensor type
242 string token;
243 ss >> token;
244 ubuntu_sensor_type type = type_from_name(token);
245 event_sensor = get(type);
246 if (event_sensor == NULL) {
247 cerr << "TestSensor ERROR: sensor does not exist, you need to create it: " << token << endl;
248 abort();
249 }
250
251 switch (type) {
252 case ubuntu_sensor_type_light:
253 ss >> event_x;
254 //cout << "got event: sensor type " << type << " (light), delay "
255 // << delay << " ms, value " << event_x << endl;
256 break;
257
258 case ubuntu_sensor_type_accelerometer:
259 ss >> event_x >> event_y >> event_z;
260 //cout << "got event: sensor type " << type << " (accel), delay "
261 // << delay << " ms, value " << event_x << "/" << event_y << "/" << event_z << endl;
262 break;
263
264 case ubuntu_sensor_type_proximity:
265 ss >> token;
266 if (token == "unknown")
267 event_distance = (UASProximityDistance) 0; // LP#1256969
268 else if (token == "near")
269 event_distance = U_PROXIMITY_NEAR;
270 else if (token == "far")
271 event_distance = U_PROXIMITY_FAR;
272 else {
273 cerr << "TestSensor ERROR: unknown proximity value " << token << endl;
274 abort();
275 }
276 //cout << "got event: sensor type " << type << " (proximity), delay "
277 // << delay << " ms, value " << int(event_distance) << endl;
278 break;
279
280 default:
281 cerr << "TestSensor ERROR: unhandled sensor type " << token << endl;
282 abort();
283 }
284
285 // wake up after given delay for committing the change and processing the
286 // next event
287 setup_timer(unsigned(delay));
288}
289
290void
291SensorController::setup_timer(unsigned delay_ms)
292{
293 static timer_t timerid; // we keep a pointer to that until on_timer
294 struct sigevent sev;
295 struct itimerspec its { {0, 0}, // interval
296 {time_t(delay_ms / 1000),
297 long((delay_ms % 1000) * 1000000L) % 1000000000L } };
298
299 sev.sigev_notify = SIGEV_THREAD;
300 sev.sigev_notify_function = SensorController::on_timer;
301 sev.sigev_notify_attributes = NULL;
302 sev.sigev_value.sival_ptr = &timerid;
303
304 if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) < 0) {
305 perror("TestSensor ERROR: Failed to create timer");
306 abort();
307 }
308 if (timer_settime(timerid, 0, &its, NULL) < 0) {
309 perror("TestSensor ERROR: Failed to set up timer");
310 abort();
311 };
312}
313
314void
315SensorController::on_timer(union sigval sval)
316{
317 timer_t* timerid = static_cast<timer_t*>(sval.sival_ptr);
318 //cout << "on_timer called\n";
319 timer_delete(*timerid);
320
321 SensorController& sc = SensorController::instance();
322
323 // update sensor values, call callback
324 if (sc.event_sensor && sc.event_sensor->enabled) {
325 sc.event_sensor->x = sc.event_x;
326 sc.event_sensor->y = sc.event_y;
327 sc.event_sensor->z = sc.event_z;
328 sc.event_sensor->distance = sc.event_distance;
329 sc.event_sensor->timestamp = chrono::duration_cast<chrono::nanoseconds>(
330 chrono::system_clock::now().time_since_epoch()).count();
331 if (sc.event_sensor->on_event_cb != NULL) {
332 //cout << "TestSensor: calling sensor callback for type " << sc.event_sensor->type << endl;
333 sc.event_sensor->on_event_cb(sc.event_sensor, sc.event_sensor->event_cb_context);
334 } else {
335 //cout << "TestSensor: sensor type " << sc.event_sensor->type << "has no callback\n";
336 }
337 } else {
338 //cout << "TestSensor: sensor type " << sc.event_sensor->type << "disabled, not processing event\n";
339 }
340
341 // read/process next event
342 if (sc.next_command())
343 sc.process_event_command();
344 else {
345 //cout << "TestSensor: script ended, no further commands\n";
346 }
347}
348
349
350/***************************************
351 *
352 * Acceleration API
353 *
354 ***************************************/
355
356UASensorsAccelerometer* ua_sensors_accelerometer_new()
357{
358 return SensorController::instance().get(ubuntu_sensor_type_accelerometer);
359}
360
361UStatus ua_sensors_accelerometer_enable(UASensorsAccelerometer* s)
362{
363 static_cast<TestSensor*>(s)->enabled = true;
364 return (UStatus) 0;
365}
366
367UStatus ua_sensors_accelerometer_disable(UASensorsAccelerometer* s)
368{
369 static_cast<TestSensor*>(s)->enabled = false;
370 return (UStatus) 0;
371}
372
373uint32_t ua_sensors_accelerometer_get_min_delay(UASensorsAccelerometer* s)
374{
375 return static_cast<TestSensor*>(s)->min_delay;
376}
377
378float ua_sensors_accelerometer_get_min_value(UASensorsAccelerometer* s) __SF_FN_ATTR;
379float ua_sensors_accelerometer_get_min_value(UASensorsAccelerometer* s)
380{
381 return static_cast<TestSensor*>(s)->min_value;
382}
383
384float ua_sensors_accelerometer_get_max_value(UASensorsAccelerometer* s) __SF_FN_ATTR;
385float ua_sensors_accelerometer_get_max_value(UASensorsAccelerometer* s)
386{
387 return static_cast<TestSensor*>(s)->max_value;
388}
389
390float ua_sensors_accelerometer_get_resolution(UASensorsAccelerometer* s) __SF_FN_ATTR;
391float ua_sensors_accelerometer_get_resolution(UASensorsAccelerometer* s)
392{
393 return static_cast<TestSensor*>(s)->resolution;
394}
395
396void ua_sensors_accelerometer_set_reading_cb(UASensorsAccelerometer* s, on_accelerometer_event_cb cb, void* ctx)
397{
398 TestSensor* sensor = static_cast<TestSensor*>(s);
399 sensor->on_event_cb = cb;
400 sensor->event_cb_context = ctx;
401}
402
403uint64_t uas_accelerometer_event_get_timestamp(UASAccelerometerEvent* e)
404{
405 return static_cast<TestSensor*>(e)->timestamp;
406}
407
408float uas_accelerometer_event_get_acceleration_x(UASAccelerometerEvent* e) __SF_FN_ATTR;
409float uas_accelerometer_event_get_acceleration_x(UASAccelerometerEvent* e)
410{
411 return static_cast<TestSensor*>(e)->x;
412}
413
414float uas_accelerometer_event_get_acceleration_y(UASAccelerometerEvent* e) __SF_FN_ATTR;
415float uas_accelerometer_event_get_acceleration_y(UASAccelerometerEvent* e)
416{
417 return static_cast<TestSensor*>(e)->y;
418}
419
420float uas_accelerometer_event_get_acceleration_z(UASAccelerometerEvent* e) __SF_FN_ATTR;
421float uas_accelerometer_event_get_acceleration_z(UASAccelerometerEvent* e)
422{
423 return static_cast<TestSensor*>(e)->z;
424}
425
426/***************************************
427 *
428 * Proximity API
429 *
430 ***************************************/
431
432UASensorsProximity* ua_sensors_proximity_new()
433{
434 return SensorController::instance().get(ubuntu_sensor_type_proximity);
435}
436
437UStatus ua_sensors_proximity_enable(UASensorsProximity* s)
438{
439 static_cast<TestSensor*>(s)->enabled = true;
440 return (UStatus) 0;
441}
442
443UStatus ua_sensors_proximity_disable(UASensorsProximity* s)
444{
445 static_cast<TestSensor*>(s)->enabled = false;
446 return (UStatus) 0;
447}
448
449uint32_t ua_sensors_proximity_get_min_delay(UASensorsProximity* s)
450{
451 return static_cast<TestSensor*>(s)->min_delay;
452}
453
454// the next three function make no sense in the API, just return zero
455float ua_sensors_proximity_get_min_value(UASensorsProximity*) __SF_FN_ATTR;
456float ua_sensors_proximity_get_min_value(UASensorsProximity*)
457{
458 return 0.0;
459}
460
461float ua_sensors_proximity_get_max_value(UASensorsProximity*) __SF_FN_ATTR;
462float ua_sensors_proximity_get_max_value(UASensorsProximity*)
463{
464 return 0.0;
465}
466
467float ua_sensors_proximity_get_resolution(UASensorsProximity*) __SF_FN_ATTR;
468float ua_sensors_proximity_get_resolution(UASensorsProximity*)
469{
470 return 0.0;
471}
472
473void ua_sensors_proximity_set_reading_cb(UASensorsProximity* s, on_proximity_event_cb cb, void* ctx)
474{
475 TestSensor* sensor = static_cast<TestSensor*>(s);
476 sensor->on_event_cb = cb;
477 sensor->event_cb_context = ctx;
478}
479
480uint64_t uas_proximity_event_get_timestamp(UASProximityEvent* e)
481{
482 return static_cast<TestSensor*>(e)->timestamp;
483}
484
485UASProximityDistance uas_proximity_event_get_distance(UASProximityEvent* e)
486{
487 return static_cast<TestSensor*>(e)->distance;
488}
489
490
491/***************************************
492 *
493 * Light API
494 *
495 ***************************************/
496
497UASensorsLight* ua_sensors_light_new()
498{
499 return SensorController::instance().get(ubuntu_sensor_type_light);
500}
501
502UStatus ua_sensors_light_enable(UASensorsLight* s)
503{
504 static_cast<TestSensor*>(s)->enabled = true;
505 return (UStatus) 0;
506}
507
508UStatus ua_sensors_light_disable(UASensorsLight* s)
509{
510 static_cast<TestSensor*>(s)->enabled = false;
511 return (UStatus) 0;
512}
513
514uint32_t ua_sensors_light_get_min_delay(UASensorsLight* s)
515{
516 return static_cast<TestSensor*>(s)->min_delay;
517}
518
519float ua_sensors_light_get_min_value(UASensorsLight* s) __SF_FN_ATTR;
520float ua_sensors_light_get_min_value(UASensorsLight* s)
521{
522 return static_cast<TestSensor*>(s)->min_value;
523}
524
525float ua_sensors_light_get_max_value(UASensorsLight* s) __SF_FN_ATTR;
526float ua_sensors_light_get_max_value(UASensorsLight* s)
527{
528 return static_cast<TestSensor*>(s)->max_value;
529}
530
531float ua_sensors_light_get_resolution(UASensorsLight* s) __SF_FN_ATTR;
532float ua_sensors_light_get_resolution(UASensorsLight* s)
533{
534 return static_cast<TestSensor*>(s)->resolution;
535}
536
537void ua_sensors_light_set_reading_cb(UASensorsLight* s, on_light_event_cb cb, void* ctx)
538{
539 TestSensor* sensor = static_cast<TestSensor*>(s);
540 sensor->on_event_cb = cb;
541 sensor->event_cb_context = ctx;
542}
543
544uint64_t uas_light_event_get_timestamp(UASLightEvent* e)
545{
546 return static_cast<TestSensor*>(e)->timestamp;
547}
548
549float uas_light_event_get_light(UASLightEvent* e) __SF_FN_ATTR;
550float uas_light_event_get_light(UASLightEvent* e)
551{
552 return static_cast<TestSensor*>(e)->x;
553}
0554
=== added directory 'tests'
=== added file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/CMakeLists.txt 2014-01-08 15:17:50 +0000
@@ -0,0 +1,30 @@
1find_package(PkgConfig REQUIRED)
2pkg_check_modules(PROCESS_CPP process-cpp REQUIRED)
3
4include_directories(${GTEST_ROOT}/src)
5include_directories(${PROCESS_CPP_INCLUDE_DIRS})
6
7set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11")
8
9add_executable(
10 test_ubuntu_application_sensors
11 test_ubuntu_application_sensors.cpp
12)
13
14target_link_libraries(
15 test_ubuntu_application_sensors
16
17 ubuntu_application_api
18 gtest
19 gtest_main
20 ${PROCESS_CPP_LIBRARIES}
21)
22
23# we need to call the tests with pointing to the locally built test platform
24# library
25add_test(
26 test_ubuntu_application_sensors
27
28 env LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/src/ubuntu/testbackend ${CMAKE_CURRENT_BINARY_DIR}/test_ubuntu_application_sensors
29)
30
031
=== added file 'tests/test_ubuntu_application_sensors.cpp'
--- tests/test_ubuntu_application_sensors.cpp 1970-01-01 00:00:00 +0000
+++ tests/test_ubuntu_application_sensors.cpp 2014-01-08 15:17:50 +0000
@@ -0,0 +1,243 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 3 as
6 * 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: Martin Pitt <martin.pitti@ubuntu.com>
17 */
18
19#include <cstdlib>
20#include <cstdio>
21#include <queue>
22#include <chrono>
23
24#include <core/testing/fork_and_run.h>
25
26#include "gtest/gtest.h"
27
28#include <ubuntu/application/sensors/accelerometer.h>
29#include <ubuntu/application/sensors/event/accelerometer.h>
30#include <ubuntu/application/sensors/proximity.h>
31#include <ubuntu/application/sensors/event/proximity.h>
32#include <ubuntu/application/sensors/light.h>
33#include <ubuntu/application/sensors/event/light.h>
34
35using namespace std;
36
37typedef chrono::time_point<chrono::system_clock,chrono::nanoseconds> time_point_system_ns;
38
39struct event {
40 uint64_t timestamp;
41 float x, y, z;
42 UASProximityDistance distance;
43 void* context;
44};
45queue<struct event> events;
46
47class APITest : public testing::Test
48{
49 protected:
50 virtual void SetUp()
51 {
52 snprintf(data_file, sizeof(data_file), "%s", "/tmp/sensor_test.XXXXXX");
53 data_fd = mkstemp(data_file);
54 if (data_fd < 0) {
55 perror("mkstemp");
56 abort();
57 }
58 setenv("UBUNTU_PLATFORM_API_SENSOR_TEST", data_file, 1);
59 setenv("UBUNTU_PLATFORM_API_BACKEND", "libubuntu_application_api_test.so.1", 1);
60
61 // ensure the queue is clear
62 while (events.size() > 0)
63 events.pop();
64 }
65
66 virtual void TearDown()
67 {
68 unlink(data_file);
69 }
70
71 void set_data(const char* data)
72 {
73 write(data_fd, data, strlen(data));
74 fsync(data_fd);
75 }
76
77 char data_file[100];
78 int data_fd;
79};
80
81// without any data, there are no sensors defined
82TESTP_F(APITest, NoData, {
83 EXPECT_EQ(NULL, ua_sensors_accelerometer_new());
84 EXPECT_EQ(NULL, ua_sensors_proximity_new());
85 EXPECT_EQ(NULL, ua_sensors_light_new());
86})
87
88TESTP_F(APITest, CreateProximity, {
89 set_data("create proximity");
90 EXPECT_EQ(NULL, ua_sensors_accelerometer_new());
91 EXPECT_EQ(NULL, ua_sensors_light_new());
92
93 UASensorsProximity *s = ua_sensors_proximity_new();
94 EXPECT_TRUE(s != NULL);
95})
96
97TESTP_F(APITest, CreateAccelerator, {
98 set_data("create accel 0.5 1000 0.1");
99 EXPECT_EQ(NULL, ua_sensors_proximity_new());
100 EXPECT_EQ(NULL, ua_sensors_light_new());
101
102 UASensorsAccelerometer *s = ua_sensors_accelerometer_new();
103 EXPECT_TRUE(s != NULL);
104 EXPECT_FLOAT_EQ(0.5, ua_sensors_accelerometer_get_min_value(s));
105 EXPECT_FLOAT_EQ(1000.0, ua_sensors_accelerometer_get_max_value(s));
106 EXPECT_FLOAT_EQ(0.1, ua_sensors_accelerometer_get_resolution(s));
107})
108
109TESTP_F(APITest, CreateLight, {
110 set_data("create light 0 10 0.5");
111 EXPECT_EQ(NULL, ua_sensors_proximity_new());
112 EXPECT_EQ(NULL, ua_sensors_accelerometer_new());
113
114 UASensorsLight *s = ua_sensors_light_new();
115 EXPECT_TRUE(s != NULL);
116 EXPECT_FLOAT_EQ(0.0, ua_sensors_light_get_min_value(s));
117 EXPECT_FLOAT_EQ(10.0, ua_sensors_light_get_max_value(s));
118 EXPECT_FLOAT_EQ(0.5, ua_sensors_light_get_resolution(s));
119})
120
121TESTP_F(APITest, ProximityEvents, {
122 set_data("create proximity\n"
123 " # some comment\n"
124 " \n"
125 "50 proximity near\n"
126 "100 proximity far\n"
127 "80 proximity unknown\n"
128 );
129
130 UASensorsProximity *s = ua_sensors_proximity_new();
131 EXPECT_TRUE(s != NULL);
132 ua_sensors_proximity_enable(s);
133 auto start_time = chrono::system_clock::now();
134
135 ua_sensors_proximity_set_reading_cb(s,
136 [](UASProximityEvent* ev, void* ctx) {
137 events.push({uas_proximity_event_get_timestamp(ev),
138 .0, .0, .0,
139 uas_proximity_event_get_distance(ev),
140 ctx});
141 }, NULL);
142
143 usleep(350000);
144 EXPECT_EQ(3, events.size());
145
146 auto e = events.front();
147 events.pop();
148 EXPECT_EQ(e.distance, U_PROXIMITY_NEAR);
149 EXPECT_EQ(NULL, e.context);
150 auto event_time = time_point_system_ns(std::chrono::nanoseconds(e.timestamp));
151 auto delay = chrono::duration_cast<chrono::milliseconds>(event_time - start_time).count();
152 EXPECT_GE(delay, 30);
153 EXPECT_LE(delay, 70);
154
155 e = events.front();
156 events.pop();
157 EXPECT_EQ(e.distance, U_PROXIMITY_FAR);
158 event_time = time_point_system_ns(std::chrono::nanoseconds(e.timestamp));
159 delay = chrono::duration_cast<chrono::milliseconds>(event_time - start_time).count();
160 EXPECT_GE(delay, 130);
161 EXPECT_LE(delay, 170);
162
163 e = events.front();
164 events.pop();
165 EXPECT_EQ(e.distance, (UASProximityDistance) 0);
166 event_time = time_point_system_ns(std::chrono::nanoseconds(e.timestamp));
167 delay = chrono::duration_cast<chrono::milliseconds>(event_time - start_time).count();
168 EXPECT_GE(delay, 210);
169 EXPECT_LE(delay, 250);
170})
171
172TESTP_F(APITest, LightEvents, {
173 set_data(" create light 0 10 1\n"
174 "1 light 5\n"
175 "100 light 8\n"
176 );
177
178 UASensorsLight *s = ua_sensors_light_new();
179 EXPECT_TRUE(s != NULL);
180 ua_sensors_light_enable(s);
181 auto start_time = chrono::system_clock::now();
182
183 ua_sensors_light_set_reading_cb(s,
184 [](UASLightEvent* ev, void* ctx) {
185 events.push({uas_light_event_get_timestamp(ev),
186 uas_light_event_get_light(ev), .0, .0,
187 (UASProximityDistance) 0, ctx});
188 }, NULL);
189
190 usleep(130000);
191 EXPECT_EQ(2, events.size());
192
193 auto e = events.front();
194 events.pop();
195 EXPECT_FLOAT_EQ(e.x, 5);
196 EXPECT_EQ(NULL, e.context);
197 auto event_time = time_point_system_ns(std::chrono::nanoseconds(e.timestamp));
198 auto delay = chrono::duration_cast<chrono::milliseconds>(event_time - start_time).count();
199 EXPECT_LE(delay, 10);
200
201 e = events.front();
202 events.pop();
203 EXPECT_FLOAT_EQ(e.x, 8);
204 event_time = time_point_system_ns(std::chrono::nanoseconds(e.timestamp));
205 delay = chrono::duration_cast<chrono::milliseconds>(event_time - start_time).count();
206 EXPECT_GE(delay, 91);
207 EXPECT_LE(delay, 111);
208})
209
210TESTP_F(APITest, AccelEvents, {
211 // cover the case of > 1 s, to ensure that we correctly do mod arithmetic
212 set_data("create accel -1000 1000 0.1\n"
213 "1100 accel 5.5 -8.5 9.9\n"
214 );
215
216 UASensorsAccelerometer *s = ua_sensors_accelerometer_new();
217 EXPECT_TRUE(s != NULL);
218 ua_sensors_accelerometer_enable(s);
219 auto start_time = chrono::system_clock::now();
220
221 ua_sensors_accelerometer_set_reading_cb(s,
222 [](UASAccelerometerEvent* ev, void* ctx) {
223 events.push({uas_accelerometer_event_get_timestamp(ev),
224 uas_accelerometer_event_get_acceleration_x(ev),
225 uas_accelerometer_event_get_acceleration_y(ev),
226 uas_accelerometer_event_get_acceleration_z(ev),
227 (UASProximityDistance) 0, ctx});
228 }, NULL);
229
230 usleep(1200000);
231 EXPECT_EQ(1, events.size());
232
233 auto e = events.front();
234 events.pop();
235 EXPECT_FLOAT_EQ(e.x, 5.5);
236 EXPECT_FLOAT_EQ(e.y, -8.5);
237 EXPECT_FLOAT_EQ(e.z, 9.9);
238 EXPECT_EQ(NULL, e.context);
239 auto event_time = time_point_system_ns(std::chrono::nanoseconds(e.timestamp));
240 auto delay = chrono::duration_cast<chrono::milliseconds>(event_time - start_time).count();
241 EXPECT_GE(delay, 1050);
242 EXPECT_LE(delay, 1150);
243})

Subscribers

People subscribed via source and target branches