diff --git a/grilo/automated/test-grilo.py b/grilo/automated/test-grilo.py new file mode 100755 index 0000000000000000000000000000000000000000..42797802270b967a4b6e9b2c8554d499bee76dfc --- /dev/null +++ b/grilo/automated/test-grilo.py @@ -0,0 +1,199 @@ +#! /usr/bin/python3 +# -*- coding: utf-8 -*- + +# Copyright © 2015 Collabora Ltd. +# +# SPDX-License-Identifier: MPL-2.0 +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import unittest +import subprocess +import shutil +import os +import sys + +from gi.repository import GLib +from gi.repository import Gio +from gi.repository import Grl +from gi.repository import GrlPls + +# import from toplevel directory +sys.path.insert(0, os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir)) +from apertis_tests_lib.grilo import GriloBrowserMixin +from apertis_tests_lib import ApertisTest +from apertis_tests_lib import LONG_JPEG_NAME + + +class GriloTest(ApertisTest, GriloBrowserMixin): + def setUp(self): + self.loop = GLib.MainLoop.new(None, False) + self.path = GLib.Dir.make_tmp('test-grilo-XXXXXX') + self.registry = Grl.Registry.get_default() + gfile = Gio.File.new_for_path(self.path) + config = Grl.Config.new('grl-filesystem', 'Filesystem') + config.set_string('base-uri', gfile.get_uri()) + self.registry.add_config(config) + self.registry.load_plugin_by_id('grl-filesystem') + self.source = self.registry.lookup_source('grl-filesystem') + self.assertIsNotNone(self.source) + + def tearDown(self): + shutil.rmtree(self.path) + + def content_changed_cb(self, source, changed_medias, change_type, + location_unknown): + self.assertEqual(source, self.source) + self.assertFalse(location_unknown) + for media in changed_medias: + print("GriloTest: received 'content-changed' signal:", + media.get_url(), change_type.value_nick) + + self.assertTrue(len(self.expectations) > 0) + expected_change_type, expected_uri = self.expectations.pop(0) + self.assertEqual(change_type, expected_change_type) + self.assertEqual(media.get_url(), expected_uri) + + if len(self.expectations) == 0: + self.loop.quit() + + def filesystem_notification_tests(self): + print("GriloTest: filesystem notifications") + + ops = self.source.supported_operations() + self.assertTrue((ops & Grl.SupportedOps.NOTIFY_CHANGE) != 0) + + self.source.notify_change_start() + signal_id = self.source.connect('content-changed', + self.content_changed_cb) + + self.expectations = [] + gfiles = [] + + # FIXME: Grilo emits change notifications with the parent directory URI + # instead of the URI of the actual media being added/removed/changed. + # At the time of writing it is unclear if it's a bug or by design. + # If upstream changes the behaviour we'll have to remove all those + # get_parent() below. + # See https://bugzilla.gnome.org/show_bug.cgi?id=755181 + + # Create ~/file + path = os.path.join(self.path, 'file') + gfile = Gio.File.new_for_path(path) + gfile.create(Gio.FileCreateFlags.REPLACE_DESTINATION, None) + gfiles.append(gfile) + self.expectations.append((Grl.SourceChangeType.ADDED, + gfile.get_parent().get_uri())) + self.loop.run() + + # Create ~/subdir + path = os.path.join(self.path, 'subdir') + gfile = Gio.File.new_for_path(path) + gfile.make_directory(None) + gfiles.append(gfile) + self.expectations.append((Grl.SourceChangeType.ADDED, + gfile.get_parent().get_uri())) + self.loop.run() + + # Create ~/subdir/file + path = os.path.join(self.path, 'subdir', 'file') + gfile = Gio.File.new_for_path(path) + gfile.create(Gio.FileCreateFlags.REPLACE_DESTINATION, None) + gfiles.append(gfile) + self.expectations.append((Grl.SourceChangeType.ADDED, + gfile.get_parent().get_uri())) + self.loop.run() + + # Create ~/subdir/subdir + path = os.path.join(self.path, 'subdir', 'subdir') + gfile = Gio.File.new_for_path(path) + gfile.make_directory(None) + gfiles.append(gfile) + self.expectations.append((Grl.SourceChangeType.ADDED, + gfile.get_parent().get_uri())) + self.loop.run() + + # Modify ~/file + path = os.path.join(self.path, 'file') + gfile = Gio.File.new_for_path(path) + iostream = gfile.open_readwrite(None) + ostream = iostream.get_output_stream() + ostream.write_all(bytes("Hello World\n", encoding='UTF-8'), None) + iostream.close(None) + self.expectations.append((Grl.SourceChangeType.CHANGED, + gfile.get_parent().get_uri())) + self.loop.run() + + # Delete everything + for gfile in reversed(gfiles): + gfile.delete(None) + self.expectations.append((Grl.SourceChangeType.REMOVED, + gfile.get_parent().get_uri())) + self.loop.run() + + self.source.disconnect(signal_id) + self.source.notify_change_stop() + + def assert_browsed(self, medias, filename, title=None): + self.assertTrue(filename in medias) + media = medias[filename] + + if title is None: + # 'file:///foo/generic.mp3' -> 'generic' + url = media.get_url() + title = url[url.rfind('/') + 1:url.rfind('.')] + + self.assertEqual(media.get_title(), title) + + def filesystem_browse_tests(self): + print("GriloTest: filesystem browse") + medias = self.browse(self.source, self.path) + self.assert_browsed(medias, 'Music/generic.mp3') + self.assert_browsed(medias, 'Music/generic.flac') + self.assert_browsed(medias, 'Music/generic-no-artwork.mp3') + self.assert_browsed(medias, 'Music/generic.oga') + self.assert_browsed(medias, 'Music/generic.wav') + self.assert_browsed(medias, 'Music/Ghosts.pls') + self.assert_browsed(medias, 'Music/Generic_Sounds.pls') + self.assert_browsed(medias, 'Music/Ghosts.m3u') + self.assert_browsed(medias, 'Pictures/' + LONG_JPEG_NAME) + self.assert_browsed(medias, 'Pictures/collabora-logo-big.png') + self.assert_browsed(medias, 'Videos/big_buck_bunny_smaller.ogv') + + def playlist_tests(self): + print("GriloTest: playlist") + ops = self.source.supported_operations() + self.assertTrue((ops & Grl.SupportedOps.MEDIA_FROM_URI) != 0) + + caps = self.source.get_caps(Grl.SupportedOps.MEDIA_FROM_URI) + options = Grl.OperationOptions.new(caps) + keys = [Grl.METADATA_KEY_TITLE, + Grl.METADATA_KEY_URL, + Grl.METADATA_KEY_MIME] + + uri = 'file://%s/Music/Generic_Sounds.pls' % self.path + media = self.source.get_media_from_uri_sync(uri, keys, options) + self.assertIsNotNone(media) + self.assertTrue(GrlPls.media_is_playlist(media)) + + medias = self.browse(self.source, self.path, media) + self.assertEqual(len(medias), 3) + self.assert_browsed(medias, 'Music/audio/generic.mp3', + "Generic Sound 1") + self.assert_browsed(medias, 'Music/audio/generic.flac', + "Generic Sound 2") + self.assert_browsed(medias, 'Music/audio/generic.oga', + "Generic Sound 3") + + # This is the only test to avoid multiple initializations of Grilo + def test_all(self): + self.filesystem_notification_tests() + self.copy_medias(self.path) + self.filesystem_browse_tests() + self.playlist_tests() + +if __name__ == "__main__": + Grl.init([]) + unittest.main()