1#!/usr/bin/env python
2# Copyright 2015 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""
7Unit tests for the contents of shared_prefs.py (mostly SharedPrefs).
8"""
9
10import logging
11import unittest
12
13from devil import devil_env
14from devil.android import device_utils
15from devil.android.sdk import shared_prefs
16
17with devil_env.SysPath(devil_env.PYMOCK_PATH):
18  import mock  # pylint: disable=import-error
19
20
21def MockDeviceWithFiles(files=None):
22  if files is None:
23    files = {}
24
25  def file_exists(path):
26    return path in files
27
28  def write_file(path, contents, **_kwargs):
29    files[path] = contents
30
31  def read_file(path, **_kwargs):
32    return files[path]
33
34  device = mock.MagicMock(spec=device_utils.DeviceUtils)
35  device.FileExists = mock.Mock(side_effect=file_exists)
36  device.WriteFile = mock.Mock(side_effect=write_file)
37  device.ReadFile = mock.Mock(side_effect=read_file)
38  return device
39
40
41class SharedPrefsTest(unittest.TestCase):
42
43  def setUp(self):
44    self.device = MockDeviceWithFiles({
45      '/data/data/com.some.package/shared_prefs/prefs.xml':
46          "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
47          '<map>\n'
48          '  <int name="databaseVersion" value="107" />\n'
49          '  <boolean name="featureEnabled" value="false" />\n'
50          '  <string name="someHashValue">249b3e5af13d4db2</string>\n'
51          '</map>'})
52    self.expected_data = {'databaseVersion': 107,
53                          'featureEnabled': False,
54                          'someHashValue': '249b3e5af13d4db2'}
55
56  def testPropertyLifetime(self):
57    prefs = shared_prefs.SharedPrefs(
58        self.device, 'com.some.package', 'prefs.xml')
59    self.assertEquals(len(prefs), 0)  # collection is empty before loading
60    prefs.SetInt('myValue', 444)
61    self.assertEquals(len(prefs), 1)
62    self.assertEquals(prefs.GetInt('myValue'), 444)
63    self.assertTrue(prefs.HasProperty('myValue'))
64    prefs.Remove('myValue')
65    self.assertEquals(len(prefs), 0)
66    self.assertFalse(prefs.HasProperty('myValue'))
67    with self.assertRaises(KeyError):
68      prefs.GetInt('myValue')
69
70  def testPropertyType(self):
71    prefs = shared_prefs.SharedPrefs(
72        self.device, 'com.some.package', 'prefs.xml')
73    prefs.SetInt('myValue', 444)
74    self.assertEquals(prefs.PropertyType('myValue'), 'int')
75    with self.assertRaises(TypeError):
76      prefs.GetString('myValue')
77    with self.assertRaises(TypeError):
78      prefs.SetString('myValue', 'hello')
79
80  def testLoad(self):
81    prefs = shared_prefs.SharedPrefs(
82        self.device, 'com.some.package', 'prefs.xml')
83    self.assertEquals(len(prefs), 0)  # collection is empty before loading
84    prefs.Load()
85    self.assertEquals(len(prefs), len(self.expected_data))
86    self.assertEquals(prefs.AsDict(), self.expected_data)
87    self.assertFalse(prefs.changed)
88
89  def testClear(self):
90    prefs = shared_prefs.SharedPrefs(
91        self.device, 'com.some.package', 'prefs.xml')
92    prefs.Load()
93    self.assertEquals(prefs.AsDict(), self.expected_data)
94    self.assertFalse(prefs.changed)
95    prefs.Clear()
96    self.assertEquals(len(prefs), 0)  # collection is empty now
97    self.assertTrue(prefs.changed)
98
99  def testCommit(self):
100    prefs = shared_prefs.SharedPrefs(
101        self.device, 'com.some.package', 'other_prefs.xml')
102    self.assertFalse(self.device.FileExists(prefs.path))  # file does not exist
103    prefs.Load()
104    self.assertEquals(len(prefs), 0)  # file did not exist, collection is empty
105    prefs.SetInt('magicNumber', 42)
106    prefs.SetFloat('myMetric', 3.14)
107    prefs.SetLong('bigNumner', 6000000000)
108    prefs.SetStringSet('apps', ['gmail', 'chrome', 'music'])
109    self.assertFalse(self.device.FileExists(prefs.path))  # still does not exist
110    self.assertTrue(prefs.changed)
111    prefs.Commit()
112    self.assertTrue(self.device.FileExists(prefs.path))  # should exist now
113    self.device.KillAll.assert_called_once_with(prefs.package, exact=True,
114                                                as_root=True, quiet=True)
115    self.assertFalse(prefs.changed)
116
117    prefs = shared_prefs.SharedPrefs(
118        self.device, 'com.some.package', 'other_prefs.xml')
119    self.assertEquals(len(prefs), 0)  # collection is empty before loading
120    prefs.Load()
121    self.assertEquals(prefs.AsDict(), {
122        'magicNumber': 42,
123        'myMetric': 3.14,
124        'bigNumner': 6000000000,
125        'apps': ['gmail', 'chrome', 'music']})  # data survived roundtrip
126
127  def testAsContextManager_onlyReads(self):
128    with shared_prefs.SharedPrefs(
129        self.device, 'com.some.package', 'prefs.xml') as prefs:
130      self.assertEquals(prefs.AsDict(), self.expected_data)  # loaded and ready
131    self.assertEquals(self.device.WriteFile.call_args_list, [])  # did not write
132
133  def testAsContextManager_readAndWrite(self):
134    with shared_prefs.SharedPrefs(
135        self.device, 'com.some.package', 'prefs.xml') as prefs:
136      prefs.SetBoolean('featureEnabled', True)
137      prefs.Remove('someHashValue')
138      prefs.SetString('newString', 'hello')
139
140    self.assertTrue(self.device.WriteFile.called)  # did write
141    with shared_prefs.SharedPrefs(
142        self.device, 'com.some.package', 'prefs.xml') as prefs:
143      # changes persisted
144      self.assertTrue(prefs.GetBoolean('featureEnabled'))
145      self.assertFalse(prefs.HasProperty('someHashValue'))
146      self.assertEquals(prefs.GetString('newString'), 'hello')
147      self.assertTrue(prefs.HasProperty('databaseVersion'))  # still there
148
149  def testAsContextManager_commitAborted(self):
150    with self.assertRaises(TypeError):
151      with shared_prefs.SharedPrefs(
152          self.device, 'com.some.package', 'prefs.xml') as prefs:
153        prefs.SetBoolean('featureEnabled', True)
154        prefs.Remove('someHashValue')
155        prefs.SetString('newString', 'hello')
156        prefs.SetInt('newString', 123)  # oops!
157
158    self.assertEquals(self.device.WriteFile.call_args_list, [])  # did not write
159    with shared_prefs.SharedPrefs(
160        self.device, 'com.some.package', 'prefs.xml') as prefs:
161      # contents were not modified
162      self.assertEquals(prefs.AsDict(), self.expected_data)
163
164if __name__ == '__main__':
165  logging.getLogger().setLevel(logging.DEBUG)
166  unittest.main(verbosity=2)
167