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