core: Add hacks for XBMC
[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_for_headers (MessageHeaders headers) throws Error {
38         throw new ClientHacksError.NA ("");
39     }
40
41     public bool is_album_art_request (Message message) {
42         return false;
43     }
44
45     public void apply (MediaItem item) {
46     }
47 }
48
49 public class Rygel.HTTPGetTest : GLib.Object {
50     protected HTTPServer server;
51     protected HTTPClient client;
52
53     private bool server_done;
54     private bool client_done;
55
56     private MainLoop main_loop;
57
58     private Error error;
59
60     public static int main (string[] args) {
61         try {
62             var test = new HTTPGetTest ();
63
64             test.run ();
65         } catch (TestError.SKIP error) {
66             return error.code;
67         } catch (Error error) {
68             critical ("%s", error.message);
69
70             return -1;
71         }
72
73         return 0;
74     }
75
76     public HTTPGetTest () throws Error {
77         this.server = new HTTPServer ();
78         this.client = new HTTPClient (this.server.context,
79                                       this.server.uri);
80         this.main_loop = new MainLoop (null, false);
81     }
82
83     public virtual void run () throws Error {
84         Timeout.add_seconds (3, this.on_timeout);
85         this.server.message_received.connect (this.on_message_received);
86         this.client.completed.connect (this.on_client_completed);
87
88         this.client.run.begin ();
89
90         this.main_loop.run ();
91
92         if (this.error != null) {
93             throw this.error;
94         }
95     }
96
97     private HTTPRequest create_request (Soup.Message msg) throws Error {
98         return new HTTPGet (this.server,
99                             this.server.context.server,
100                             msg);
101     }
102
103     private void on_client_completed (StateMachine client) {
104         if (this.server_done) {
105             this.main_loop.quit ();
106         }
107
108         this.client_done = true;
109     }
110
111     private void on_message_received (HTTPServer   server,
112                                       Soup.Message msg) {
113         this.handle_client_message.begin (msg);
114     }
115
116     private async void handle_client_message (Soup.Message msg) {
117         try {
118             var request = this.create_request (msg);
119
120             yield request.run ();
121
122             assert ((request as HTTPGet).item != null);
123
124             if (this.client_done) {
125                 this.main_loop.quit ();
126             }
127
128             this.server_done = true;
129         } catch (Error error) {
130             this.error = error;
131             this.main_loop.quit ();
132
133             return;
134         }
135     }
136
137     private bool on_timeout () {
138         this.error = new TestError.TIMEOUT ("Timeout");
139         this.main_loop.quit ();
140
141         return false;
142     }
143 }
144
145 public class Rygel.HTTPServer : GLib.Object {
146     private const string SERVER_PATH = "/RygelHTTPServer/Rygel/Test";
147     public string path_root {
148         get {
149             return SERVER_PATH;
150         }
151     }
152
153     public MediaContainer root_container;
154     public GUPnP.Context context;
155
156     public string uri {
157         owned get {
158             var item_uri = new HTTPItemURI (this.root_container.ITEM_ID,
159                                             this);
160
161             return item_uri.to_string ();
162         }
163     }
164
165     public signal void message_received (Soup.Message message);
166
167     public HTTPServer () throws TestError {
168         try {
169             this.context = new GUPnP.Context (null, "lo", 0);
170         } catch (Error error) {
171             throw new TestError.SKIP ("Network context not available");
172         }
173
174         assert (this.context != null);
175         assert (this.context.host_ip != null);
176         assert (this.context.port > 0);
177
178         this.context.server.add_handler (SERVER_PATH, this.server_cb);
179
180         this.root_container = new MediaContainer ();
181     }
182
183     private void server_cb (Server        server,
184                             Soup.Message  msg,
185                             string        path,
186                             HashTable?    query,
187                             ClientContext client) {
188         this.context.server.pause_message (msg);
189         this.message_received (msg);
190     }
191
192     public Transcoder get_transcoder (string target) throws Error {
193         return new Transcoder ();
194     }
195 }
196
197 public class Rygel.HTTPClient : GLib.Object, StateMachine {
198     public GUPnP.Context context;
199     public Soup.Message msg;
200
201     public Cancellable cancellable { get; set; }
202
203     public HTTPClient (GUPnP.Context context,
204                        string        uri) {
205         this.context = context;
206
207         this.msg = new Soup.Message ("GET",  uri);
208         assert (this.msg != null);
209     }
210
211     public async void run () {
212         SourceFunc run_continue = run.callback;
213
214         this.context.session.queue_message (this.msg, (session, msg) => {
215             run_continue ();
216         });
217
218         yield;
219
220         this.completed ();
221     }
222 }
223
224 public class Rygel.MediaContainer : Rygel.MediaObject {
225     public const string ITEM_ID = "TestItem";
226
227     public async MediaObject? find_object (string       item_id,
228                                            Cancellable? cancellable)
229                                            throws Error {
230         SourceFunc find_object_continue = find_object.callback;
231         Idle.add (() => {
232             find_object_continue ();
233
234             return false;
235         });
236
237         yield;
238
239         if (item_id == ITEM_ID) {
240             return new VideoItem ();
241         } else {
242             return null;
243         }
244     }
245 }
246
247 internal abstract class Rygel.HTTPGetHandler {
248     public HTTPResponse render_body (HTTPGet get_request) {
249         return new HTTPResponse (get_request);
250     }
251
252     public void add_response_headers (HTTPGet get_request) {}
253 }
254
255 internal class Rygel.HTTPTranscodeHandler : Rygel.HTTPGetHandler {
256     public HTTPTranscodeHandler (Transcoder  transcoder,
257                                  Cancellable cancellable) {}
258 }
259
260 internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
261     public HTTPIdentityHandler (Cancellable cancellable) {}
262 }
263
264 public abstract class Rygel.MediaItem : Rygel.MediaObject {
265     public long size = 1024;
266     public ArrayList<Subtitle> subtitles = new ArrayList<Subtitle> ();
267     public ArrayList<Thumbnail> thumbnails = new ArrayList<Thumbnail> ();
268
269     public bool place_holder = false;
270
271     public bool is_live_stream () {
272         return true;
273     }
274
275     public bool streamable () {
276         return true;
277     }
278 }
279
280 private class Rygel.AudioItem : MediaItem {
281     public int64 duration = 2048;
282 }
283
284 private interface Rygel.VisualItem : MediaItem {
285     public abstract int width { get; set; }
286     public abstract int height { get; set; }
287     public abstract int pixel_width { get; set; }
288     public abstract int pixel_height { get; set; }
289     public abstract int color_depth { get; set; }
290
291     public abstract ArrayList<Thumbnail> thumbnails { get; protected set; }
292 }
293
294 private class Rygel.VideoItem : AudioItem, VisualItem {
295     public int width { get; set; default = -1; }
296     public int height { get; set; default = -1; }
297     public int pixel_width { get; set; default = -1; }
298     public int pixel_height { get; set; default = -1; }
299     public int color_depth { get; set; default = -1; }
300
301     public ArrayList<Thumbnail> thumbnails { get; protected set; }
302     public ArrayList<Subtitle> subtitles;
303 }
304
305 private class Rygel.MusicItem : AudioItem {
306     public Thumbnail album_art;
307 }
308
309 public class Rygel.Thumbnail {
310     public long size = 1024;
311 }
312
313 public class Rygel.Subtitle {
314     public long size = 1024;
315 }
316
317 internal class Rygel.HTTPResponse : Rygel.StateMachine, GLib.Object {
318     public Cancellable cancellable { get; set; }
319
320     private Soup.Message msg;
321     private Soup.Server server;
322
323     public HTTPResponse (HTTPGet get_request) {
324         this.msg = get_request.msg;
325         this.msg.response_headers.set_encoding (Soup.Encoding.CONTENT_LENGTH);
326         this.server = get_request.server;
327     }
328
329     public async void run () {
330         SourceFunc run_continue = run.callback;
331
332         Idle.add (() => {
333             run_continue ();
334
335             return false;
336         });
337
338         yield;
339
340         this.msg.set_status (Soup.KnownStatusCode.OK);
341         this.server.unpause_message (msg);
342
343         this.completed ();
344     }
345 }
346
347 public class Rygel.MediaObject {
348     public string id;
349 }
350
351 public class Rygel.Transcoder {}