You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
481 lines
19 KiB
Python
481 lines
19 KiB
Python
import unittest
|
|
import os
|
|
from mpos.config import SharedPreferences, Editor
|
|
|
|
|
|
class TestSharedPreferences(unittest.TestCase):
|
|
"""Test suite for SharedPreferences configuration storage."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures before each test method."""
|
|
self.test_app_name = "com.test.unittest"
|
|
self.test_dir = f"data/{self.test_app_name}"
|
|
self.test_file = f"{self.test_dir}/config.json"
|
|
# Clean up any existing test data
|
|
self._cleanup()
|
|
|
|
def tearDown(self):
|
|
"""Clean up test fixtures after each test method."""
|
|
self._cleanup()
|
|
|
|
def _cleanup(self):
|
|
"""Remove test data directory if it exists."""
|
|
try:
|
|
# Use os.stat() instead of os.path.exists() for MicroPython compatibility
|
|
try:
|
|
os.stat(self.test_file)
|
|
os.remove(self.test_file)
|
|
except OSError:
|
|
pass # File doesn't exist, that's fine
|
|
|
|
try:
|
|
os.stat(self.test_dir)
|
|
os.rmdir(self.test_dir)
|
|
except OSError:
|
|
pass # Directory doesn't exist, that's fine
|
|
|
|
try:
|
|
os.stat("data")
|
|
# Try to remove data directory, but it might have other contents
|
|
try:
|
|
os.rmdir("data")
|
|
except OSError:
|
|
# Directory not empty, that's okay
|
|
pass
|
|
except OSError:
|
|
pass # Directory doesn't exist, that's fine
|
|
except Exception as e:
|
|
# Cleanup failure is not critical for tests
|
|
print(f"Cleanup warning: {e}")
|
|
|
|
# ============================================================
|
|
# Basic String Operations
|
|
# ============================================================
|
|
|
|
def test_put_get_string(self):
|
|
"""Test storing and retrieving a string value."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
editor = prefs.edit()
|
|
editor.put_string("username", "testuser")
|
|
editor.commit()
|
|
|
|
# Reload to verify persistence
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("username"), "testuser")
|
|
|
|
def test_get_string_default(self):
|
|
"""Test getting a string with default value when key doesn't exist."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs.get_string("nonexistent", "default"), "default")
|
|
self.assertIsNone(prefs.get_string("nonexistent"))
|
|
|
|
def test_put_string_overwrites(self):
|
|
"""Test that putting a string overwrites existing value."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_string("key", "value1").commit()
|
|
prefs.edit().put_string("key", "value2").commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("key"), "value2")
|
|
|
|
# ============================================================
|
|
# Integer Operations
|
|
# ============================================================
|
|
|
|
def test_put_get_int(self):
|
|
"""Test storing and retrieving an integer value."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
editor = prefs.edit()
|
|
editor.put_int("count", 42)
|
|
editor.commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_int("count"), 42)
|
|
|
|
def test_get_int_default(self):
|
|
"""Test getting an integer with default value when key doesn't exist."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs.get_int("nonexistent", 100), 100)
|
|
self.assertEqual(prefs.get_int("nonexistent"), 0)
|
|
|
|
def test_get_int_invalid_type(self):
|
|
"""Test getting an integer when stored value is invalid."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_string("invalid", "not_a_number").commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_int("invalid", 99), 99)
|
|
|
|
# ============================================================
|
|
# Boolean Operations
|
|
# ============================================================
|
|
|
|
def test_put_get_bool(self):
|
|
"""Test storing and retrieving boolean values."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_bool("enabled", True).commit()
|
|
prefs.edit().put_bool("disabled", False).commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertTrue(prefs2.get_bool("enabled"))
|
|
self.assertFalse(prefs2.get_bool("disabled"))
|
|
|
|
def test_get_bool_default(self):
|
|
"""Test getting a boolean with default value when key doesn't exist."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
self.assertTrue(prefs.get_bool("nonexistent", True))
|
|
self.assertFalse(prefs.get_bool("nonexistent", False))
|
|
self.assertFalse(prefs.get_bool("nonexistent"))
|
|
|
|
# ============================================================
|
|
# List Operations
|
|
# ============================================================
|
|
|
|
def test_put_get_list(self):
|
|
"""Test storing and retrieving a list."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
test_list = [1, 2, 3, "four"]
|
|
prefs.edit().put_list("mylist", test_list).commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_list("mylist"), test_list)
|
|
|
|
def test_get_list_default(self):
|
|
"""Test getting a list with default value when key doesn't exist."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs.get_list("nonexistent"), [])
|
|
self.assertEqual(prefs.get_list("nonexistent", ["a", "b"]), ["a", "b"])
|
|
|
|
def test_append_to_list(self):
|
|
"""Test appending items to a list."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
editor = prefs.edit()
|
|
editor.append_to_list("items", {"id": 1, "name": "first"})
|
|
editor.append_to_list("items", {"id": 2, "name": "second"})
|
|
editor.commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
items = prefs2.get_list("items")
|
|
self.assertEqual(len(items), 2)
|
|
self.assertEqual(items[0]["id"], 1)
|
|
self.assertEqual(items[1]["name"], "second")
|
|
|
|
def test_update_list_item(self):
|
|
"""Test updating an item in a list."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_list("items", [{"a": 1}, {"b": 2}]).commit()
|
|
|
|
prefs.edit().update_list_item("items", 1, {"b": 99}).commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
items = prefs2.get_list("items")
|
|
self.assertEqual(items[1]["b"], 99)
|
|
|
|
def test_remove_from_list(self):
|
|
"""Test removing an item from a list."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_list("items", [{"a": 1}, {"b": 2}, {"c": 3}]).commit()
|
|
|
|
prefs.edit().remove_from_list("items", 1).commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
items = prefs2.get_list("items")
|
|
self.assertEqual(len(items), 2)
|
|
self.assertEqual(items[0]["a"], 1)
|
|
self.assertEqual(items[1]["c"], 3)
|
|
|
|
def test_get_list_item(self):
|
|
"""Test getting a specific field from a list item."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_list("users", [
|
|
{"name": "Alice", "age": 30},
|
|
{"name": "Bob", "age": 25}
|
|
]).commit()
|
|
|
|
self.assertEqual(prefs.get_list_item("users", 0, "name"), "Alice")
|
|
self.assertEqual(prefs.get_list_item("users", 1, "age"), 25)
|
|
self.assertIsNone(prefs.get_list_item("users", 99, "name"))
|
|
self.assertEqual(prefs.get_list_item("users", 99, "name", "default"), "default")
|
|
|
|
def test_get_list_item_dict(self):
|
|
"""Test getting an entire dictionary from a list."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_list("configs", [{"key": "value"}]).commit()
|
|
|
|
item = prefs.get_list_item_dict("configs", 0)
|
|
self.assertEqual(item["key"], "value")
|
|
self.assertEqual(prefs.get_list_item_dict("configs", 99), {})
|
|
|
|
# ============================================================
|
|
# Dictionary Operations
|
|
# ============================================================
|
|
|
|
def test_put_get_dict(self):
|
|
"""Test storing and retrieving a dictionary."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
test_dict = {"key1": "value1", "key2": 42}
|
|
prefs.edit().put_dict("mydict", test_dict).commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_dict("mydict"), test_dict)
|
|
|
|
def test_get_dict_default(self):
|
|
"""Test getting a dict with default value when key doesn't exist."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs.get_dict("nonexistent"), {})
|
|
self.assertEqual(prefs.get_dict("nonexistent", {"default": True}), {"default": True})
|
|
|
|
def test_put_dict_item(self):
|
|
"""Test adding items to a dictionary."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
editor = prefs.edit()
|
|
editor.put_dict_item("wifi_aps", "HomeNetwork", {"password": "secret123", "priority": 1})
|
|
editor.put_dict_item("wifi_aps", "WorkNetwork", {"password": "work456", "priority": 2})
|
|
editor.commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_dict_item("wifi_aps", "HomeNetwork")["password"], "secret123")
|
|
self.assertEqual(prefs2.get_dict_item("wifi_aps", "WorkNetwork")["priority"], 2)
|
|
|
|
def test_remove_dict_item(self):
|
|
"""Test removing an item from a dictionary."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_dict("items", {"a": 1, "b": 2, "c": 3}).commit()
|
|
|
|
prefs.edit().remove_dict_item("items", "b").commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
items = prefs2.get_dict("items")
|
|
self.assertEqual(len(items), 2)
|
|
self.assertIn("a", items)
|
|
self.assertIn("c", items)
|
|
self.assertFalse("b" in items) # Use 'in' operator instead of assertNotIn
|
|
|
|
def test_get_dict_item(self):
|
|
"""Test getting a specific item from a dictionary."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_dict("settings", {
|
|
"theme": {"color": "blue", "size": 14},
|
|
"audio": {"volume": 80}
|
|
}).commit()
|
|
|
|
theme = prefs.get_dict_item("settings", "theme")
|
|
self.assertEqual(theme["color"], "blue")
|
|
self.assertEqual(prefs.get_dict_item("settings", "nonexistent"), {})
|
|
|
|
def test_get_dict_item_field(self):
|
|
"""Test getting a specific field from a dict item."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_dict("networks", {
|
|
"ssid1": {"password": "pass1", "signal": 100},
|
|
"ssid2": {"password": "pass2", "signal": 50}
|
|
}).commit()
|
|
|
|
self.assertEqual(prefs.get_dict_item_field("networks", "ssid1", "password"), "pass1")
|
|
self.assertEqual(prefs.get_dict_item_field("networks", "ssid2", "signal"), 50)
|
|
self.assertIsNone(prefs.get_dict_item_field("networks", "ssid99", "password"))
|
|
self.assertEqual(prefs.get_dict_item_field("networks", "ssid1", "missing", "def"), "def")
|
|
|
|
def test_get_dict_keys(self):
|
|
"""Test getting all keys from a dictionary."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_dict("items", {"a": 1, "b": 2, "c": 3}).commit()
|
|
|
|
keys = prefs.get_dict_keys("items")
|
|
self.assertEqual(len(keys), 3)
|
|
self.assertIn("a", keys)
|
|
self.assertIn("b", keys)
|
|
self.assertIn("c", keys)
|
|
|
|
def test_get_dict_keys_nonexistent(self):
|
|
"""Test getting keys from a nonexistent dictionary."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs.get_dict_keys("nonexistent"), [])
|
|
|
|
# ============================================================
|
|
# Editor Operations
|
|
# ============================================================
|
|
|
|
def test_editor_chaining(self):
|
|
"""Test that editor methods can be chained."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit()\
|
|
.put_string("name", "test")\
|
|
.put_int("count", 5)\
|
|
.put_bool("enabled", True)\
|
|
.commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("name"), "test")
|
|
self.assertEqual(prefs2.get_int("count"), 5)
|
|
self.assertTrue(prefs2.get_bool("enabled"))
|
|
|
|
def test_editor_apply_vs_commit(self):
|
|
"""Test that both apply and commit save data."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
|
|
# Test apply
|
|
prefs.edit().put_string("key1", "apply_test").apply()
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("key1"), "apply_test")
|
|
|
|
# Test commit
|
|
prefs.edit().put_string("key2", "commit_test").commit()
|
|
prefs3 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs3.get_string("key2"), "commit_test")
|
|
|
|
def test_editor_without_commit_doesnt_save(self):
|
|
"""Test that changes without commit/apply are not persisted."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
editor = prefs.edit()
|
|
editor.put_string("unsaved", "value")
|
|
# Don't call commit() or apply()
|
|
|
|
# Reload and verify data wasn't saved
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertIsNone(prefs2.get_string("unsaved"))
|
|
|
|
def test_multiple_edits(self):
|
|
"""Test multiple sequential edit operations."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
|
|
prefs.edit().put_string("step", "1").commit()
|
|
prefs.edit().put_string("step", "2").commit()
|
|
prefs.edit().put_string("step", "3").commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("step"), "3")
|
|
|
|
# ============================================================
|
|
# File Persistence
|
|
# ============================================================
|
|
|
|
def test_directory_creation(self):
|
|
"""Test that directory structure is created automatically."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_string("test", "value").commit()
|
|
|
|
# Use os.stat() instead of os.path.exists() for MicroPython
|
|
try:
|
|
os.stat("data")
|
|
data_exists = True
|
|
except OSError:
|
|
data_exists = False
|
|
self.assertTrue(data_exists)
|
|
|
|
try:
|
|
os.stat(self.test_dir)
|
|
dir_exists = True
|
|
except OSError:
|
|
dir_exists = False
|
|
self.assertTrue(dir_exists)
|
|
|
|
try:
|
|
os.stat(self.test_file)
|
|
file_exists = True
|
|
except OSError:
|
|
file_exists = False
|
|
self.assertTrue(file_exists)
|
|
|
|
def test_custom_filename(self):
|
|
"""Test using a custom filename for preferences."""
|
|
prefs = SharedPreferences(self.test_app_name, "custom.json")
|
|
prefs.edit().put_string("custom", "data").commit()
|
|
|
|
custom_file = f"{self.test_dir}/custom.json"
|
|
# Use os.stat() instead of os.path.exists() for MicroPython
|
|
try:
|
|
os.stat(custom_file)
|
|
file_exists = True
|
|
except OSError:
|
|
file_exists = False
|
|
self.assertTrue(file_exists)
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name, "custom.json")
|
|
self.assertEqual(prefs2.get_string("custom"), "data")
|
|
|
|
def test_load_existing_file(self):
|
|
"""Test loading from an existing preferences file."""
|
|
# Create initial prefs
|
|
prefs1 = SharedPreferences(self.test_app_name)
|
|
prefs1.edit().put_string("existing", "data").commit()
|
|
|
|
# Load in a new instance
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("existing"), "data")
|
|
|
|
# ============================================================
|
|
# Edge Cases and Error Handling
|
|
# ============================================================
|
|
|
|
def test_empty_string_values(self):
|
|
"""Test storing and retrieving empty strings."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_string("empty", "").commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("empty"), "")
|
|
|
|
def test_zero_values(self):
|
|
"""Test storing and retrieving zero values."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_int("zero", 0).put_bool("false", False).commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_int("zero"), 0)
|
|
self.assertFalse(prefs2.get_bool("false"))
|
|
|
|
def test_none_values(self):
|
|
"""Test handling None values gracefully."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
# Getting a nonexistent key should return None or default
|
|
self.assertIsNone(prefs.get_string("nonexistent"))
|
|
|
|
def test_special_characters_in_keys(self):
|
|
"""Test keys with special characters."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit()\
|
|
.put_string("key.with.dots", "value1")\
|
|
.put_string("key_with_underscores", "value2")\
|
|
.put_string("key-with-dashes", "value3")\
|
|
.commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("key.with.dots"), "value1")
|
|
self.assertEqual(prefs2.get_string("key_with_underscores"), "value2")
|
|
self.assertEqual(prefs2.get_string("key-with-dashes"), "value3")
|
|
|
|
def test_unicode_values(self):
|
|
"""Test storing and retrieving Unicode strings."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
prefs.edit().put_string("unicode", "Hello δΈη π").commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
self.assertEqual(prefs2.get_string("unicode"), "Hello δΈη π")
|
|
|
|
def test_large_nested_structure(self):
|
|
"""Test storing a complex nested data structure."""
|
|
prefs = SharedPreferences(self.test_app_name)
|
|
complex_data = {
|
|
"users": {
|
|
"alice": {"age": 30, "roles": ["admin", "user"]},
|
|
"bob": {"age": 25, "roles": ["user"]}
|
|
},
|
|
"settings": {
|
|
"theme": "dark",
|
|
"notifications": True,
|
|
"limits": [10, 20, 30]
|
|
}
|
|
}
|
|
prefs.edit().put_dict("app_data", complex_data).commit()
|
|
|
|
prefs2 = SharedPreferences(self.test_app_name)
|
|
loaded = prefs2.get_dict("app_data")
|
|
self.assertEqual(loaded["users"]["alice"]["age"], 30)
|
|
self.assertEqual(loaded["settings"]["theme"], "dark")
|
|
self.assertEqual(loaded["settings"]["limits"][2], 30)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|