• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""
6Unit tests for the contents of shared_prefs.py (mostly SharedPrefs).
7"""
8
9import logging
10import unittest
11
12from devil import devil_env
13from devil.android import device_utils
14from devil.android.sdk import shared_prefs
15from devil.android.sdk import version_codes
16
17with devil_env.SysPath(devil_env.PYMOCK_PATH):
18  import mock  # pylint: disable=import-error
19
20INITIAL_XML = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
21               '<map>\n'
22               '  <int name="databaseVersion" value="107" />\n'
23               '  <boolean name="featureEnabled" value="false" />\n'
24               '  <string name="someHashValue">249b3e5af13d4db2</string>\n'
25               '</map>')
26
27
28def MockDeviceWithFiles(files=None):
29  if files is None:
30    files = {}
31
32  def file_exists(path):
33    return path in files
34
35  def write_file(path, contents, **_kwargs):
36    files[path] = contents
37
38  def read_file(path, **_kwargs):
39    return files[path]
40
41  device = mock.MagicMock(spec=device_utils.DeviceUtils)
42  device.FileExists = mock.Mock(side_effect=file_exists)
43  device.WriteFile = mock.Mock(side_effect=write_file)
44  device.ReadFile = mock.Mock(side_effect=read_file)
45  return device
46
47
48class SharedPrefsTest(unittest.TestCase):
49  def setUp(self):
50    self.device = MockDeviceWithFiles({
51        '/data/data/com.some.package/shared_prefs/prefs.xml': INITIAL_XML
52    })
53    self.expected_data = {
54        'databaseVersion': 107,
55        'featureEnabled': False,
56        'someHashValue': '249b3e5af13d4db2'
57    }
58
59  def testPropertyLifetime(self):
60    prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
61                                     'prefs.xml')
62    self.assertEquals(len(prefs), 0)  # collection is empty before loading
63    prefs.SetInt('myValue', 444)
64    self.assertEquals(len(prefs), 1)
65    self.assertEquals(prefs.GetInt('myValue'), 444)
66    self.assertTrue(prefs.HasProperty('myValue'))
67    prefs.Remove('myValue')
68    self.assertEquals(len(prefs), 0)
69    self.assertFalse(prefs.HasProperty('myValue'))
70    with self.assertRaises(KeyError):
71      prefs.GetInt('myValue')
72
73  def testPropertyType(self):
74    prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
75                                     'prefs.xml')
76    prefs.SetInt('myValue', 444)
77    self.assertEquals(prefs.PropertyType('myValue'), 'int')
78    with self.assertRaises(TypeError):
79      prefs.GetString('myValue')
80    with self.assertRaises(TypeError):
81      prefs.SetString('myValue', 'hello')
82
83  def testLoad(self):
84    prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
85                                     'prefs.xml')
86    self.assertEquals(len(prefs), 0)  # collection is empty before loading
87    prefs.Load()
88    self.assertEquals(len(prefs), len(self.expected_data))
89    self.assertEquals(prefs.AsDict(), self.expected_data)
90    self.assertFalse(prefs.changed)
91
92  def testClear(self):
93    prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
94                                     'prefs.xml')
95    prefs.Load()
96    self.assertEquals(prefs.AsDict(), self.expected_data)
97    self.assertFalse(prefs.changed)
98    prefs.Clear()
99    self.assertEquals(len(prefs), 0)  # collection is empty now
100    self.assertTrue(prefs.changed)
101
102  def testCommit(self):
103    type(self.device).build_version_sdk = mock.PropertyMock(
104        return_value=version_codes.LOLLIPOP_MR1)
105    prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
106                                     'other_prefs.xml')
107    self.assertFalse(self.device.FileExists(prefs.path))  # file does not exist
108    prefs.Load()
109    self.assertEquals(len(prefs), 0)  # file did not exist, collection is empty
110    prefs.SetInt('magicNumber', 42)
111    prefs.SetFloat('myMetric', 3.14)
112    prefs.SetLong('bigNumner', 6000000000)
113    prefs.SetStringSet('apps', ['gmail', 'chrome', 'music'])
114    self.assertFalse(self.device.FileExists(prefs.path))  # still does not exist
115    self.assertTrue(prefs.changed)
116    prefs.Commit()
117    self.assertTrue(self.device.FileExists(prefs.path))  # should exist now
118    self.device.KillAll.assert_called_once_with(
119        prefs.package, exact=True, as_root=True, quiet=True)
120    self.assertFalse(prefs.changed)
121
122    prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
123                                     'other_prefs.xml')
124    self.assertEquals(len(prefs), 0)  # collection is empty before loading
125    prefs.Load()
126    self.assertEquals(
127        prefs.AsDict(), {
128            'magicNumber': 42,
129            'myMetric': 3.14,
130            'bigNumner': 6000000000,
131            'apps': ['gmail', 'chrome', 'music']
132        })  # data survived roundtrip
133
134  def testForceCommit(self):
135    type(self.device).build_version_sdk = mock.PropertyMock(
136        return_value=version_codes.LOLLIPOP_MR1)
137    prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
138                                     'prefs.xml')
139    prefs.Load()
140    new_xml = 'Not valid XML'
141    self.device.WriteFile('/data/data/com.some.package/shared_prefs/prefs.xml',
142                          new_xml)
143    prefs.Commit()
144    # Since we didn't change anything, Commit() should be a no-op.
145    self.assertEquals(
146        self.device.ReadFile(
147            '/data/data/com.some.package/shared_prefs/prefs.xml'), new_xml)
148    prefs.Commit(force_commit=True)
149    # Forcing the commit should restore the originally read XML.
150    self.assertEquals(
151        self.device.ReadFile(
152            '/data/data/com.some.package/shared_prefs/prefs.xml'), INITIAL_XML)
153
154  def testAsContextManager_onlyReads(self):
155    with shared_prefs.SharedPrefs(self.device, 'com.some.package',
156                                  'prefs.xml') as prefs:
157      self.assertEquals(prefs.AsDict(), self.expected_data)  # loaded and ready
158    self.assertEquals(self.device.WriteFile.call_args_list, [])  # did not write
159
160  def testAsContextManager_readAndWrite(self):
161    type(self.device).build_version_sdk = mock.PropertyMock(
162        return_value=version_codes.LOLLIPOP_MR1)
163    with shared_prefs.SharedPrefs(self.device, 'com.some.package',
164                                  'prefs.xml') as prefs:
165      prefs.SetBoolean('featureEnabled', True)
166      prefs.Remove('someHashValue')
167      prefs.SetString('newString', 'hello')
168
169    self.assertTrue(self.device.WriteFile.called)  # did write
170    with shared_prefs.SharedPrefs(self.device, 'com.some.package',
171                                  'prefs.xml') as prefs:
172      # changes persisted
173      self.assertTrue(prefs.GetBoolean('featureEnabled'))
174      self.assertFalse(prefs.HasProperty('someHashValue'))
175      self.assertEquals(prefs.GetString('newString'), 'hello')
176      self.assertTrue(prefs.HasProperty('databaseVersion'))  # still there
177
178  def testAsContextManager_commitAborted(self):
179    with self.assertRaises(TypeError):
180      with shared_prefs.SharedPrefs(self.device, 'com.some.package',
181                                    'prefs.xml') as prefs:
182        prefs.SetBoolean('featureEnabled', True)
183        prefs.Remove('someHashValue')
184        prefs.SetString('newString', 'hello')
185        prefs.SetInt('newString', 123)  # oops!
186
187    self.assertEquals(self.device.WriteFile.call_args_list, [])  # did not write
188    with shared_prefs.SharedPrefs(self.device, 'com.some.package',
189                                  'prefs.xml') as prefs:
190      # contents were not modified
191      self.assertEquals(prefs.AsDict(), self.expected_data)
192
193  def testEncryptedPath(self):
194    type(self.device).build_version_sdk = mock.PropertyMock(
195        return_value=version_codes.MARSHMALLOW)
196    with shared_prefs.SharedPrefs(
197        self.device, 'com.some.package', 'prefs.xml',
198        use_encrypted_path=True) as prefs:
199      self.assertTrue(prefs.path.startswith('/data/data'))
200
201    type(self.device).build_version_sdk = mock.PropertyMock(
202        return_value=version_codes.NOUGAT)
203    with shared_prefs.SharedPrefs(
204        self.device, 'com.some.package', 'prefs.xml',
205        use_encrypted_path=True) as prefs:
206      self.assertTrue(prefs.path.startswith('/data/user_de/0'))
207
208
209if __name__ == '__main__':
210  logging.getLogger().setLevel(logging.DEBUG)
211  unittest.main(verbosity=2)
212