1# -*- Mode: Python; py-indent-offset: 4 -*- 2# vim: tabstop=4 shiftwidth=4 expandtab 3 4import os 5import unittest 6import warnings 7 8import pytest 9 10import gi.overrides 11from gi import PyGIWarning 12from gi.repository import GLib, Gio 13 14from .helper import ignore_gi_deprecation_warnings 15 16 17class TestGio(unittest.TestCase): 18 def test_file_enumerator(self): 19 self.assertEqual(Gio.FileEnumerator, gi.overrides.Gio.FileEnumerator) 20 f = Gio.file_new_for_path("./") 21 22 iter_info = [] 23 for info in f.enumerate_children("standard::*", 0, None): 24 iter_info.append(info.get_name()) 25 26 next_info = [] 27 enumerator = f.enumerate_children("standard::*", 0, None) 28 while True: 29 info = enumerator.next_file(None) 30 if info is None: 31 break 32 next_info.append(info.get_name()) 33 34 self.assertEqual(iter_info, next_info) 35 36 def test_menu_item(self): 37 menu = Gio.Menu() 38 item = Gio.MenuItem() 39 item.set_attribute([("label", "s", "Test"), 40 ("action", "s", "app.test")]) 41 menu.append_item(item) 42 value = menu.get_item_attribute_value(0, "label", GLib.VariantType.new("s")) 43 self.assertEqual("Test", value.unpack()) 44 value = menu.get_item_attribute_value(0, "action", GLib.VariantType.new("s")) 45 self.assertEqual("app.test", value.unpack()) 46 47 def test_volume_monitor_warning(self): 48 with warnings.catch_warnings(record=True) as warn: 49 warnings.simplefilter('always') 50 Gio.VolumeMonitor() 51 self.assertEqual(len(warn), 1) 52 self.assertTrue(issubclass(warn[0].category, PyGIWarning)) 53 self.assertRegexpMatches(str(warn[0].message), 54 '.*Gio\\.VolumeMonitor\\.get\\(\\).*') 55 56 57class TestGSettings(unittest.TestCase): 58 def setUp(self): 59 self.settings = Gio.Settings.new('org.gnome.test') 60 # we change the values in the tests, so set them to predictable start 61 # value 62 self.settings.reset('test-string') 63 self.settings.reset('test-array') 64 self.settings.reset('test-boolean') 65 self.settings.reset('test-enum') 66 67 def test_iter(self): 68 assert set(list(self.settings)) == set([ 69 'test-tuple', 'test-array', 'test-boolean', 'test-string', 70 'test-enum', 'test-range']) 71 72 def test_get_set(self): 73 for key in self.settings: 74 old_value = self.settings[key] 75 self.settings[key] = old_value 76 assert self.settings[key] == old_value 77 78 def test_native(self): 79 self.assertTrue('test-array' in self.settings.list_keys()) 80 81 # get various types 82 v = self.settings.get_value('test-boolean') 83 self.assertEqual(v.get_boolean(), True) 84 self.assertEqual(self.settings.get_boolean('test-boolean'), True) 85 86 v = self.settings.get_value('test-string') 87 self.assertEqual(v.get_string(), 'Hello') 88 self.assertEqual(self.settings.get_string('test-string'), 'Hello') 89 90 v = self.settings.get_value('test-array') 91 self.assertEqual(v.unpack(), [1, 2]) 92 93 v = self.settings.get_value('test-tuple') 94 self.assertEqual(v.unpack(), (1, 2)) 95 96 v = self.settings.get_value('test-range') 97 assert v.unpack() == 123 98 99 # set a value 100 self.settings.set_string('test-string', 'World') 101 self.assertEqual(self.settings.get_string('test-string'), 'World') 102 103 self.settings.set_value('test-string', GLib.Variant('s', 'Goodbye')) 104 self.assertEqual(self.settings.get_string('test-string'), 'Goodbye') 105 106 def test_constructor(self): 107 # default constructor uses path from schema 108 self.assertEqual(self.settings.get_property('path'), '/tests/') 109 110 # optional constructor arguments 111 with_path = Gio.Settings.new_with_path('org.gnome.nopathtest', '/mypath/') 112 self.assertEqual(with_path.get_property('path'), '/mypath/') 113 self.assertEqual(with_path['np-int'], 42) 114 115 def test_dictionary_api(self): 116 self.assertEqual(len(self.settings), 6) 117 self.assertTrue('test-array' in self.settings) 118 self.assertTrue('test-array' in self.settings.keys()) 119 self.assertFalse('nonexisting' in self.settings) 120 self.assertFalse(4 in self.settings) 121 self.assertEqual(bool(self.settings), True) 122 123 def test_get(self): 124 self.assertEqual(self.settings['test-boolean'], True) 125 self.assertEqual(self.settings['test-string'], 'Hello') 126 self.assertEqual(self.settings['test-enum'], 'banana') 127 self.assertEqual(self.settings['test-array'], [1, 2]) 128 self.assertEqual(self.settings['test-tuple'], (1, 2)) 129 130 self.assertRaises(KeyError, self.settings.__getitem__, 'unknown') 131 self.assertRaises(KeyError, self.settings.__getitem__, 2) 132 133 def test_set(self): 134 self.settings['test-boolean'] = False 135 self.assertEqual(self.settings['test-boolean'], False) 136 self.settings['test-string'] = 'Goodbye' 137 self.assertEqual(self.settings['test-string'], 'Goodbye') 138 self.settings['test-array'] = [3, 4, 5] 139 self.assertEqual(self.settings['test-array'], [3, 4, 5]) 140 self.settings['test-enum'] = 'pear' 141 self.assertEqual(self.settings['test-enum'], 'pear') 142 143 self.assertRaises(TypeError, self.settings.__setitem__, 'test-string', 1) 144 self.assertRaises(ValueError, self.settings.__setitem__, 'test-enum', 'plum') 145 self.assertRaises(KeyError, self.settings.__setitem__, 'unknown', 'moo') 146 147 def test_set_range(self): 148 self.settings['test-range'] = 7 149 assert self.settings['test-range'] == 7 150 self.settings['test-range'] = 65535 151 assert self.settings['test-range'] == 65535 152 153 with pytest.raises(ValueError, match=".*7 - 65535.*"): 154 self.settings['test-range'] = 7 - 1 155 156 with pytest.raises(ValueError, match=".*7 - 65535.*"): 157 self.settings['test-range'] = 65535 + 1 158 159 def test_empty(self): 160 empty = Gio.Settings.new_with_path('org.gnome.empty', '/tests/') 161 self.assertEqual(len(empty), 0) 162 self.assertEqual(bool(empty), True) 163 self.assertEqual(empty.keys(), []) 164 165 @ignore_gi_deprecation_warnings 166 def test_change_event(self): 167 changed_log = [] 168 change_event_log = [] 169 170 def on_changed(settings, key): 171 changed_log.append((settings, key)) 172 173 def on_change_event(settings, keys, n_keys): 174 change_event_log.append((settings, keys, n_keys)) 175 176 self.settings.connect('changed', on_changed) 177 self.settings.connect('change-event', on_change_event) 178 self.settings['test-string'] = 'Moo' 179 self.assertEqual(changed_log, [(self.settings, 'test-string')]) 180 self.assertEqual(change_event_log, [(self.settings, 181 [GLib.quark_from_static_string('test-string')], 182 1)]) 183 184 185@unittest.skipIf(os.name == "nt", "FIXME") 186class TestGFile(unittest.TestCase): 187 def setUp(self): 188 self.file, self.io_stream = Gio.File.new_tmp('TestGFile.XXXXXX') 189 190 def tearDown(self): 191 try: 192 self.file.delete(None) 193 # test_delete and test_delete_async already remove it 194 except GLib.GError: 195 pass 196 197 def test_replace_contents(self): 198 content = b'hello\0world\x7F!' 199 succ, etag = self.file.replace_contents(content, None, False, 200 Gio.FileCreateFlags.NONE, None) 201 new_succ, new_content, new_etag = self.file.load_contents(None) 202 203 self.assertTrue(succ) 204 self.assertTrue(new_succ) 205 self.assertEqual(etag, new_etag) 206 self.assertEqual(content, new_content) 207 208 # https://bugzilla.gnome.org/show_bug.cgi?id=690525 209 def disabled_test_replace_contents_async(self): 210 content = b''.join(bytes(chr(i), 'utf-8') for i in range(128)) 211 212 def callback(f, result, d): 213 # Quit so in case of failed assertations loop doesn't keep running. 214 main_loop.quit() 215 succ, etag = self.file.replace_contents_finish(result) 216 new_succ, new_content, new_etag = self.file.load_contents(None) 217 d['succ'], d['etag'] = self.file.replace_contents_finish(result) 218 load = self.file.load_contents(None) 219 d['new_succ'], d['new_content'], d['new_etag'] = load 220 221 data = {} 222 self.file.replace_contents_async(content, None, False, 223 Gio.FileCreateFlags.NONE, None, 224 callback, data) 225 main_loop = GLib.MainLoop() 226 main_loop.run() 227 self.assertTrue(data['succ']) 228 self.assertTrue(data['new_succ']) 229 self.assertEqual(data['etag'], data['new_etag']) 230 self.assertEqual(content, data['new_content']) 231 232 def test_tmp_exists(self): 233 # A simple test to check if Gio.File.new_tmp is working correctly. 234 self.assertTrue(self.file.query_exists(None)) 235 236 def test_delete(self): 237 self.file.delete(None) 238 self.assertFalse(self.file.query_exists(None)) 239 240 def test_delete_async(self): 241 def callback(f, result, data): 242 main_loop.quit() 243 244 self.file.delete_async(0, None, callback, None) 245 main_loop = GLib.MainLoop() 246 main_loop.run() 247 self.assertFalse(self.file.query_exists(None)) 248 249 250@unittest.skipIf(os.name == "nt", "crashes on Windows") 251class TestGApplication(unittest.TestCase): 252 def test_command_line(self): 253 class App(Gio.Application): 254 args = None 255 256 def __init__(self): 257 super(App, self).__init__(flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) 258 259 def do_command_line(self, cmdline): 260 self.args = cmdline.get_arguments() 261 return 42 262 263 app = App() 264 res = app.run(['spam', 'eggs']) 265 266 self.assertEqual(res, 42) 267 self.assertSequenceEqual(app.args, ['spam', 'eggs']) 268 269 def test_local_command_line(self): 270 class App(Gio.Application): 271 local_args = None 272 273 def __init__(self): 274 super(App, self).__init__(flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) 275 276 def do_local_command_line(self, args): 277 self.local_args = args[:] # copy 278 args.remove('eggs') 279 280 # True skips do_command_line being called. 281 return True, args, 42 282 283 app = App() 284 res = app.run(['spam', 'eggs']) 285 286 self.assertEqual(res, 42) 287 self.assertSequenceEqual(app.local_args, ['spam', 'eggs']) 288 289 def test_local_and_remote_command_line(self): 290 class App(Gio.Application): 291 args = None 292 local_args = None 293 294 def __init__(self): 295 super(App, self).__init__(flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) 296 297 def do_command_line(self, cmdline): 298 self.args = cmdline.get_arguments() 299 return 42 300 301 def do_local_command_line(self, args): 302 self.local_args = args[:] # copy 303 args.remove('eggs') 304 305 # False causes do_command_line to be called with args. 306 return False, args, 0 307 308 app = App() 309 res = app.run(['spam', 'eggs']) 310 311 self.assertEqual(res, 42) 312 self.assertSequenceEqual(app.args, ['spam']) 313 self.assertSequenceEqual(app.local_args, ['spam', 'eggs']) 314 315 @unittest.skipUnless(hasattr(Gio.Application, 'add_main_option'), 316 'Requires newer version of GLib') 317 def test_add_main_option(self): 318 stored_options = [] 319 320 def on_handle_local_options(app, options): 321 stored_options.append(options) 322 return 0 # Return 0 if options have been handled 323 324 def on_activate(app): 325 pass 326 327 app = Gio.Application() 328 app.add_main_option(long_name='string', 329 short_name=b's', 330 flags=0, 331 arg=GLib.OptionArg.STRING, 332 description='some string') 333 334 app.connect('activate', on_activate) 335 app.connect('handle-local-options', on_handle_local_options) 336 app.run(['app', '-s', 'test string']) 337 338 self.assertEqual(len(stored_options), 1) 339 options = stored_options[0] 340 self.assertTrue(options.contains('string')) 341 self.assertEqual(options.lookup_value('string').unpack(), 342 'test string') 343