tests: Avoid unused methods warnings
[profile/ivi/rygel.git] / tests / rygel-http-get-test.vala
1 /*
2  * Copyright (C) 2010 Nokia Corporation.
3  *
4  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
5  *                               <zeeshan.ali@nokia.com>
6  *
7  * This file is part of Rygel.
8  *
9  * Rygel is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Rygel is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23
24 using Soup;
25 using Gee;
26
27 public errordomain Rygel.TestError {
28     SKIP = 77,
29     TIMEOUT
30 }
31
32 public errordomain Rygel.ClientHacksError {
33     NA
34 }
35
36 public class Rygel.ClientHacks {
37     public static ClientHacks create (Message? message) throws Error {
38         var headers = message.request_headers;
39         if (headers.get_one ("clienthacks.test.rygel") != null) {
40             return new ClientHacks ();
41         } else {
42             throw new ClientHacksError.NA ("");
43         }
44     }
45
46     public void apply (MediaItem? item) {
47     }
48 }
49
50 public class Rygel.TestRequestFactory {
51     public Soup.Message msg;
52     public Soup.KnownStatusCode expected_code;
53
54     public TestRequestFactory (Soup.Message msg,
55                                Soup.KnownStatusCode expected_code) {
56         this.msg = msg;
57         this.expected_code = expected_code;
58     }
59
60     internal HTTPGet create_get (HTTPServer http_server,
61                                  Soup.Server server,
62                                  Soup.Message msg) {
63         HTTPGet request = new HTTPGet (http_server, server, msg);
64         request.handler = null;
65
66         return request;
67     }
68 }
69
70 public class Rygel.HTTPGetTest : GLib.Object {
71     protected HTTPServer server;
72     protected HTTPClient client;
73
74     private bool server_done;
75     private bool client_done;
76
77     private MainLoop main_loop;
78
79     private Error error;
80
81     private ArrayList<TestRequestFactory> requests;
82     private TestRequestFactory current_request;
83
84     public static int main (string[] args) {
85         try {
86             var test = new HTTPGetTest ();
87
88             test.run ();
89         } catch (TestError.SKIP error) {
90             return error.code;
91         } catch (Error error) {
92             critical ("%s", error.message);
93
94             return -1;
95         }
96
97         /* Avoid some warnings about unused methods: */
98         var item = new VideoItem();
99         assert (!item.is_live_stream());
100         assert (!item.streamable());
101
102         return 0;
103     }
104
105     public HTTPGetTest () throws Error {
106         this.server = new HTTPServer ();
107         this.client = new HTTPClient (this.server.context);
108         this.main_loop = new MainLoop (null, false);
109         this.create_test_messages();
110     }
111
112     public virtual void run () throws Error {
113         Timeout.add_seconds (3, this.on_timeout);
114         this.server.message_received.connect (this.on_message_received);
115         this.client.completed.connect (this.on_client_completed);
116
117         this.start_next_test_request ();
118
119         this.main_loop.run ();
120
121         if (this.error != null) {
122             throw this.error;
123         }
124     }
125
126     private void create_test_messages () {
127         requests = new ArrayList<TestRequestFactory> ();
128
129         Soup.Message request = new Soup.Message ("POST", this.server.uri);
130         requests.add (new TestRequestFactory (request,
131                       Soup.KnownStatusCode.BAD_REQUEST));
132
133         request = new Soup.Message ("HEAD", this.server.uri);
134         requests.add (new TestRequestFactory (request, Soup.KnownStatusCode.OK));
135
136         request = new Soup.Message ("GET", this.server.uri);
137         requests.add (new TestRequestFactory (request, Soup.KnownStatusCode.OK));
138
139         string uri = this.server.create_uri ("VideoItem");
140         uri = uri + "/tr/MP3";
141         request = new Soup.Message ("HEAD", uri);
142         requests.add (new TestRequestFactory (request, Soup.KnownStatusCode.OK));
143
144         request = new Soup.Message ("GET", this.server.uri);
145         request.request_headers.append ("transferMode.dlna.org", "Streaming");
146         requests.add (new TestRequestFactory (request, Soup.KnownStatusCode.OK));
147
148         request = new Soup.Message ("GET", this.server.uri);
149         request.request_headers.append ("transferMode.dlna.org", "Interactive");
150         requests.add (new TestRequestFactory (request,
151                       Soup.KnownStatusCode.NOT_ACCEPTABLE));
152
153         request = new Soup.Message ("GET", this.server.uri);
154         request.request_headers.append ("Range", "bytes=1-2");
155         requests.add (new TestRequestFactory (request,
156                       Soup.KnownStatusCode.OK));
157
158         uri = this.server.create_uri ("AudioItem");
159         uri = uri + "/th/0";
160
161         request = new Soup.Message ("GET", uri);
162         requests.add (new TestRequestFactory (request,
163                       Soup.KnownStatusCode.NOT_FOUND));
164
165         request = new Soup.Message ("GET", this.server.uri);
166         request.request_headers.append ("TimeSeekRange.dlna.org", "0");
167         requests.add (new TestRequestFactory (request,
168                       Soup.KnownStatusCode.NOT_ACCEPTABLE));
169
170         uri = this.server.create_uri ("AudioItem");
171         request = new Soup.Message ("GET", uri);
172         request.request_headers.append ("TimeSeekRange.dlna.org", "0");
173         requests.add (new TestRequestFactory (request,
174                       Soup.KnownStatusCode.BAD_REQUEST));
175
176         uri = this.server.create_uri ("AudioItem");
177         request = new Soup.Message ("GET", uri);
178         request.request_headers.append ("TimeSeekRange.dlna.org", "npt=1-2049");
179         requests.add (new TestRequestFactory (request,
180                       Soup.KnownStatusCode.REQUESTED_RANGE_NOT_SATISFIABLE));
181
182         request = new Soup.Message ("GET", this.server.uri);
183         request.request_headers.append ("clienthacks.test.rygel", "f");
184         requests.add (new TestRequestFactory (request,
185                       Soup.KnownStatusCode.OK));
186
187         request = new Soup.Message ("GET", this.server.uri);
188         request.request_headers.append ("clienthacks.test.rygel", "t");
189         requests.add (new TestRequestFactory (request,
190                       Soup.KnownStatusCode.OK));
191     }
192
193     private HTTPGet create_request (Soup.Message msg) throws Error {
194         HTTPGet request = this.current_request.create_get (this.server,
195                             this.server.context.server, msg);
196         return request;
197     }
198
199     private void on_client_completed (StateMachine client) {
200         if (requests.size > 0) {
201             this.start_next_test_request ();
202         } else {
203             this.main_loop.quit ();
204             this.client_done = true;
205         }
206     }
207
208     private void start_next_test_request() {
209         this.current_request = requests.remove_at (0);
210         this.client.msg = this.current_request.msg;
211         this.client.run.begin ();
212     }
213
214     private void on_message_received (HTTPServer   server,
215                                       Soup.Message msg) {
216         this.handle_client_message.begin (msg);
217     }
218
219     private async void handle_client_message (Soup.Message msg) {
220         try {
221             var request = this.create_request (msg);
222
223             yield request.run ();
224
225             assert ((request as HTTPGet).object != null);
226
227             debug ("status.code: %d", (int) msg.status_code);
228             assert (msg.status_code == this.current_request.expected_code);
229
230             if (this.client_done) {
231                 this.main_loop.quit ();
232             }
233
234             this.server_done = true;
235         } catch (Error error) {
236             this.error = error;
237             this.main_loop.quit ();
238
239             return;
240         }
241     }
242
243     private bool on_timeout () {
244         this.error = new TestError.TIMEOUT ("Timeout");
245         this.main_loop.quit ();
246
247         return false;
248     }
249 }
250
251 public class Rygel.HTTPServer : GLib.Object {
252     private const string SERVER_PATH = "/RygelHTTPServer/Rygel/Test";
253     public string path_root {
254         get {
255             return SERVER_PATH;
256         }
257     }
258
259     public MediaContainer root_container;
260     public GUPnP.Context context;
261
262     public string uri {
263         owned get {
264             return create_uri("VideoItem");
265         }
266     }
267
268     public string create_uri (string item_id) {
269         var item = new VideoItem ();
270         item.id = item_id;
271
272         var item_uri = new HTTPItemURI (item, this);
273
274         return item_uri.to_string ();
275     }
276
277     public signal void message_received (Soup.Message message);
278
279     public HTTPServer () throws TestError {
280         try {
281             this.context = new GUPnP.Context (null, "lo", 0);
282         } catch (Error error) {
283             throw new TestError.SKIP ("Network context not available");
284         }
285
286         assert (this.context != null);
287         assert (this.context.host_ip != null);
288         assert (this.context.port > 0);
289
290         this.context.server.add_handler (SERVER_PATH, this.server_cb);
291
292         this.root_container = new MediaContainer ();
293     }
294
295     private void server_cb (Server        server,
296                             Soup.Message  msg,
297                             string        path,
298                             HashTable?    query,
299                             ClientContext client) {
300         this.context.server.pause_message (msg);
301         this.message_received (msg);
302     }
303
304     public Transcoder get_transcoder (string target) throws Error {
305         if (target == "MP3") {
306             return new Transcoder ("mp3");
307         }
308         throw new HTTPRequestError.NOT_FOUND (
309                             "No transcoder available for target format '%s'",
310                             target);
311     }
312 }
313
314 public class Rygel.HTTPClient : GLib.Object, StateMachine {
315     public GUPnP.Context context;
316     public Soup.Message msg;
317
318     public Cancellable cancellable { get; set; }
319
320     public HTTPClient (GUPnP.Context context) {
321         this.context = context;
322     }
323
324     public async void run () {
325         SourceFunc run_continue = run.callback;
326
327         this.context.session.queue_message (this.msg, (session, msg) => {
328             run_continue ();
329         });
330
331         yield;
332
333         this.completed ();
334     }
335 }
336
337 public class Rygel.MediaContainer : Rygel.MediaObject {
338
339     public async MediaObject? find_object (string       item_id,
340                                            Cancellable? cancellable)
341                                            throws Error {
342         SourceFunc find_object_continue = find_object.callback;
343         Idle.add (() => {
344             find_object_continue ();
345
346             return false;
347         });
348
349         yield;
350
351         debug ("item id: %s", item_id);
352         if (item_id == "VideoItem") {
353             return new VideoItem ();
354         } else if (item_id == "AudioItem") {
355             return new AudioItem ();
356         } else {
357             return null;
358         }
359     }
360 }
361
362 internal abstract class Rygel.HTTPGetHandler {
363     public HTTPResponse render_body (HTTPGet get_request) {
364         return new HTTPResponse (get_request);
365     }
366
367     public void add_response_headers (HTTPGet get_request) {}
368
369     public bool knows_size (HTTPGet request) { return false; }
370 }
371
372 internal class Rygel.HTTPTranscodeHandler : Rygel.HTTPGetHandler {
373     public HTTPTranscodeHandler (Transcoder  transcoder,
374                                  Cancellable cancellable) {}
375 }
376
377 internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
378     public HTTPIdentityHandler (Cancellable cancellable) {}
379 }
380
381 internal class Rygel.HTTPPlaylistHandler : Rygel.HTTPGetHandler {
382     public HTTPPlaylistHandler (Cancellable cancellable) {}
383 }
384
385 public abstract class Rygel.MediaItem : Rygel.MediaObject {
386     public long size = 1024;
387     public ArrayList<string> uris = new ArrayList<string> ();
388
389     public bool place_holder = false;
390
391     public bool is_live_stream () {
392         if (this.id == "VideoItem")
393             return false;
394         else
395             return true;
396     }
397
398     public bool streamable () {
399         return true;
400     }
401 }
402
403 private class Rygel.AudioItem : MediaItem {
404     public int64 duration = 2048;
405
406     public AudioItem () {
407         this.id = "AudioItem";
408     }
409 }
410
411 private interface Rygel.VisualItem : MediaItem {
412     public abstract int width { get; set; }
413     public abstract int height { get; set; }
414     public abstract int color_depth { get; set; }
415
416     public abstract ArrayList<Thumbnail> thumbnails { get; protected set; }
417
418     public bool is_live_stream () {
419         return false;
420     }
421
422     public bool streamable () {
423         return false;
424     }
425 }
426
427 private class Rygel.VideoItem : AudioItem, VisualItem {
428     public int width { get; set; default = -1; }
429     public int height { get; set; default = -1; }
430     public int color_depth { get; set; default = -1; }
431
432     private ArrayList<Thumbnail> ts;
433
434     public VideoItem () {
435         this.id = "VideoItem";
436     }
437
438     public ArrayList<Thumbnail> thumbnails {
439         get {
440             this.ts = new ArrayList<Thumbnail>();
441             ts.add(new Rygel.Thumbnail());
442             return this.ts;
443         }
444
445         protected set {}
446     }
447
448     public ArrayList<Subtitle> subtitles = new ArrayList<Subtitle> ();
449 }
450
451 private class Rygel.MusicItem : AudioItem {
452     public Thumbnail album_art;
453 }
454
455 public class Rygel.Thumbnail {
456     public long size = 1024;
457     public string file_extension;
458 }
459
460 public class Rygel.Subtitle {
461     public long size = 1024;
462     public string caption_type;
463 }
464
465 internal class Rygel.HTTPResponse : Rygel.StateMachine, GLib.Object {
466     public Cancellable cancellable { get; set; }
467
468     private Soup.Message msg;
469     private Soup.Server server;
470
471     public HTTPResponse (HTTPGet get_request) {
472         this.msg = get_request.msg;
473         this.msg.response_headers.set_encoding (Soup.Encoding.CONTENT_LENGTH);
474         this.server = get_request.server;
475     }
476
477     public async void run () {
478         SourceFunc run_continue = run.callback;
479
480         Idle.add (() => {
481             run_continue ();
482
483             return false;
484         });
485
486         yield;
487
488         this.msg.set_status (Soup.KnownStatusCode.OK);
489         this.server.unpause_message (msg);
490
491         this.completed ();
492     }
493 }
494
495 public class Rygel.MediaObject {
496     public string id;
497     public string mime_type = "";
498 }
499
500 public class Rygel.Transcoder : GLib.Object {
501     public string extension { get; protected set; }
502
503     public Transcoder (string extension) {
504         this.extension = extension;
505     }
506 }