GCC Code Coverage Report


Directory: ./
File: libfprint/drivers/virtual-image.c
Date: 2024-09-16 14:36:32
Exec Total Coverage
Lines: 119 130 91.5%
Functions: 13 13 100.0%
Branches: 40 69 58.0%

Line Branch Exec Source
1 /*
2 * Virtual driver for image device debugging
3 *
4 * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /*
22 * This is a virtual driver to debug the image based drivers. A small
23 * python script is provided to connect to it via a socket, allowing
24 * prints to be sent to this device programmatically.
25 * Using this it is possible to test libfprint and fprintd.
26 */
27
28 #define FP_COMPONENT "virtual_image"
29
30 #include "fpi-log.h"
31
32 #include "virtual-device-private.h"
33
34 #include "../fpi-image.h"
35 #include "../fpi-image-device.h"
36
37 struct _FpDeviceVirtualImage
38 {
39 FpImageDevice parent;
40
41 FpiDeviceVirtualListener *listener;
42 GCancellable *cancellable;
43
44 gboolean automatic_finger;
45 FpImage *recv_img;
46 gint recv_img_hdr[2];
47 };
48
49 G_DECLARE_FINAL_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FPI, DEVICE_VIRTUAL_IMAGE, FpImageDevice)
50
4/5
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 120 times.
✗ Branch 4 not taken.
768 G_DEFINE_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FP_TYPE_IMAGE_DEVICE)
51
52 static void recv_image (FpDeviceVirtualImage *self);
53
54 static void
55 33 recv_image_img_recv_cb (GObject *source_object,
56 GAsyncResult *res,
57 gpointer user_data)
58 {
59 33 g_autoptr(GError) error = NULL;
60 33 FpiDeviceVirtualListener *listener = FPI_DEVICE_VIRTUAL_LISTENER (source_object);
61 33 FpDeviceVirtualImage *self;
62 33 FpImageDevice *device;
63 33 gsize bytes;
64
65 33 bytes = fpi_device_virtual_listener_read_finish (listener, res, &error);
66
67
3/6
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 33 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 33 times.
66 if (!bytes || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
68 33 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED))
69 return;
70
71 33 self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
72 33 device = FP_IMAGE_DEVICE (self);
73
74
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 5 times.
33 if (self->automatic_finger)
75 28 fpi_image_device_report_finger_status (device, TRUE);
76 33 fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img));
77
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 5 times.
33 if (self->automatic_finger)
78 28 fpi_image_device_report_finger_status (device, FALSE);
79
80 /* And, listen for more images from the same client. */
81
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
33 recv_image (self);
82 }
83
84 static void
85 80 recv_image_hdr_recv_cb (GObject *source_object,
86 GAsyncResult *res,
87 gpointer user_data)
88 {
89 80 g_autoptr(GError) error = NULL;
90 80 FpDeviceVirtualImage *self;
91 80 FpiDeviceVirtualListener *listener = FPI_DEVICE_VIRTUAL_LISTENER (source_object);
92 80 gsize bytes;
93
94 80 bytes = fpi_device_virtual_listener_read_finish (listener, res, &error);
95
96
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 57 times.
80 if (error)
97 {
98
4/4
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 5 times.
42 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
99
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
33 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED) ||
100 14 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
101 23 return;
102
103 g_warning ("Error receiving header for image data: %s", error->message);
104 return;
105 }
106
107
1/2
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
57 if (!bytes)
108 return;
109
110 57 self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
111
2/4
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 57 times.
57 if (self->recv_img_hdr[0] > 5000 || self->recv_img_hdr[1] > 5000)
112 {
113 g_warning ("Image header suggests an unrealistically large image, disconnecting client.");
114 fpi_device_virtual_listener_connection_close (listener);
115 }
116
117
3/4
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
57 if (self->recv_img_hdr[0] < 0 || self->recv_img_hdr[1] < 0)
118 {
119
4/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
24 switch (self->recv_img_hdr[0])
120 {
121 2 case -1:
122 /* -1 is a retry error, just pass it through */
123 2 fpi_image_device_retry_scan (FP_IMAGE_DEVICE (self), self->recv_img_hdr[1]);
124 2 break;
125
126 2 case -2:
127 /* -2 is a fatal error, just pass it through*/
128 4 fpi_image_device_session_error (FP_IMAGE_DEVICE (self),
129 2 fpi_device_error_new (self->recv_img_hdr[1]));
130 2 break;
131
132 10 case -3:
133 /* -3 sets/clears automatic finger detection for images */
134 10 self->automatic_finger = !!self->recv_img_hdr[1];
135 10 break;
136
137 10 case -4:
138 /* -4 submits a finger detection report */
139 10 fpi_image_device_report_finger_status (FP_IMAGE_DEVICE (self),
140 10 !!self->recv_img_hdr[1]);
141 10 break;
142
143 case -5:
144 /* -5 causes the device to disappear (no further data) */
145 fpi_device_remove (FP_DEVICE (self));
146 break;
147
148 default:
149 /* disconnect client, it didn't play fair */
150 fpi_device_virtual_listener_connection_close (listener);
151 }
152
153 /* And, listen for more images from the same client. */
154 24 recv_image (self);
155 24 return;
156 }
157
158 33 self->recv_img = fp_image_new (self->recv_img_hdr[0], self->recv_img_hdr[1]);
159 33 g_debug ("image data: %p", self->recv_img->data);
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 fpi_device_virtual_listener_read (listener,
161 TRUE,
162 33 (guint8 *) self->recv_img->data,
163 33 self->recv_img->width * self->recv_img->height,
164 recv_image_img_recv_cb,
165 self);
166 }
167
168 static void
169 80 recv_image (FpDeviceVirtualImage *self)
170 {
171 80 fpi_device_virtual_listener_read (self->listener,
172 TRUE,
173 80 self->recv_img_hdr,
174 sizeof (self->recv_img_hdr),
175 recv_image_hdr_recv_cb,
176 self);
177 80 }
178
179 static void
180 4 on_listener_connected (FpiDeviceVirtualListener *listener,
181 gpointer user_data)
182 {
183 4 FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
184 4 FpiImageDeviceState state;
185
186 4 self->automatic_finger = TRUE;
187
188 4 g_object_get (self,
189 "fpi-image-device-state", &state,
190 NULL);
191
192
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 switch (state)
193 {
194 4 case FPI_IMAGE_DEVICE_STATE_IDLE:
195 case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
196 case FPI_IMAGE_DEVICE_STATE_CAPTURE:
197 case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF:
198 4 recv_image (self);
199
200 case FPI_IMAGE_DEVICE_STATE_INACTIVE:
201 case FPI_IMAGE_DEVICE_STATE_ACTIVATING:
202 case FPI_IMAGE_DEVICE_STATE_DEACTIVATING:
203 default:
204 4 break;
205 }
206 4 }
207
208 static void
209 26 dev_init (FpImageDevice *dev)
210 {
211 26 g_autoptr(GError) error = NULL;
212
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
26 g_autoptr(FpiDeviceVirtualListener) listener = NULL;
213
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
26 g_autoptr(GCancellable) cancellable = NULL;
214 26 FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
215
216 26 G_DEBUG_HERE ();
217
218 26 listener = fpi_device_virtual_listener_new ();
219 26 cancellable = g_cancellable_new ();
220
221
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 if (!fpi_device_virtual_listener_start (listener,
222 26 fpi_device_get_virtual_env (FP_DEVICE (self)),
223 cancellable,
224 on_listener_connected,
225 self,
226 &error))
227 {
228 fpi_image_device_open_complete (dev, g_steal_pointer (&error));
229 return;
230 }
231
232 26 self->listener = g_steal_pointer (&listener);
233 26 self->cancellable = g_steal_pointer (&cancellable);
234
235 /* Delay result to open up the possibility of testing race conditions. */
236
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_open_complete, NULL, NULL);
237 }
238
239 static void
240 26 dev_deinit (FpImageDevice *dev)
241 {
242 26 FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
243
244 26 G_DEBUG_HERE ();
245
246 26 g_cancellable_cancel (self->cancellable);
247
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 g_clear_object (&self->cancellable);
248
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 g_clear_object (&self->listener);
249
250 /* Delay result to open up the possibility of testing race conditions. */
251 26 fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_close_complete, NULL, NULL);
252 26 }
253
254 static void
255 19 dev_activate (FpImageDevice *dev)
256 {
257 19 FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
258
259 /* Start reading (again). */
260 19 recv_image (self);
261
262 19 fpi_image_device_activate_complete (dev, NULL);
263 19 }
264
265 static void
266 19 dev_deactivate (FpImageDevice *dev)
267 {
268 19 fpi_image_device_deactivate_complete (dev, NULL);
269 19 }
270
271 static void
272 5 dev_notify_removed_cb (FpDevice *dev)
273 {
274 5 FpiImageDeviceState state;
275 5 gboolean removed;
276
277 5 g_object_get (dev,
278 "fpi-image-device-state", &state,
279 "removed", &removed,
280 NULL);
281
282
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
5 if (!removed || state == FPI_IMAGE_DEVICE_STATE_INACTIVE)
283 4 return;
284
285 /* This error will be converted to an FP_DEVICE_ERROR_REMOVED by the
286 * surrounding layers. */
287 1 fpi_image_device_session_error (FP_IMAGE_DEVICE (dev),
288 fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
289 }
290
291 static void
292 29 fpi_device_virtual_image_init (FpDeviceVirtualImage *self)
293 {
294 /* NOTE: This is not nice, but we can generally rely on the underlying
295 * system to throw errors on the transport layer.
296 */
297 29 g_signal_connect (self,
298 "notify::removed",
299 G_CALLBACK (dev_notify_removed_cb),
300 NULL);
301 29 }
302
303 static const FpIdEntry driver_ids[] = {
304 { .virtual_envvar = "FP_VIRTUAL_IMAGE" },
305 { .virtual_envvar = NULL }
306 };
307
308 static void
309 120 fpi_device_virtual_image_class_init (FpDeviceVirtualImageClass *klass)
310 {
311 120 FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
312 120 FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
313
314 120 dev_class->id = FP_COMPONENT;
315 120 dev_class->full_name = "Virtual image device for debugging";
316 120 dev_class->type = FP_DEVICE_TYPE_VIRTUAL;
317 120 dev_class->id_table = driver_ids;
318
319 120 img_class->img_open = dev_init;
320 120 img_class->img_close = dev_deinit;
321
322 120 img_class->activate = dev_activate;
323 120 img_class->deactivate = dev_deactivate;
324 }
325