544 lines
20 KiB
Python
544 lines
20 KiB
Python
import json
|
|
import os.path
|
|
import unittest
|
|
from copy import deepcopy
|
|
from unittest.mock import patch
|
|
from i3pystatus.core.color import ColorRangeModule
|
|
from i3pystatus.buds import Buds, BudsEqualizer, BudsPlacementStatus
|
|
|
|
|
|
class TestBuds(unittest.TestCase):
|
|
def setUp(self):
|
|
self.buds = Buds()
|
|
with open(os.path.join(os.path.dirname(__file__), 'test_buds.json'), 'rb') as file:
|
|
self.payload = json.load(file)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_run_device_connected(self, mock_run):
|
|
# Setup: Use json.dumps as we expect JSON
|
|
payload = self.payload.get('connected_payload')
|
|
mock_run.return_value.out = json.dumps(payload)
|
|
|
|
# Action: Call run() and save return for comparison
|
|
buds_run_return = self.buds.run()
|
|
|
|
# Verify: Assert called with right params
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} status -o json -q")
|
|
|
|
expected_output = {
|
|
"full_text": "Buds2 LW53 48RW",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: Assert correct output
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
# Verify: run() return is equal to payload
|
|
self.assertDictEqual(payload.get('payload'), buds_run_return)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_run_device_disconnected(self, mock_run):
|
|
# Setup: Use json.dumps as we expect JSON
|
|
mock_run.return_value.out = json.dumps(self.payload.get('disconnected_payload'))
|
|
|
|
# Action: Call run() and save return for comparison
|
|
buds_run_return = self.buds.run()
|
|
|
|
# Verify: Assert called with right params
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} status -o json -q")
|
|
|
|
expected_output = {
|
|
"full_text": "Disconnected",
|
|
"color": self.buds.disconnected_color
|
|
}
|
|
|
|
# Verify: Assert correct output
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
# Verify: run() return should be none
|
|
self.assertIsNone(buds_run_return)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_toggle_amb(self, mock_run):
|
|
# Setup: AMB is initially disabled
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
modified_payload['payload']['ambient_sound_enabled'] = False
|
|
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Toggle AMB
|
|
self.buds.toggle_amb()
|
|
|
|
# Verify: The correct command is sent to enable AMB
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} set ambientsound 1")
|
|
|
|
# Setup: Change the payload again to update the AMB status
|
|
modified_payload['payload']['ambient_sound_enabled'] = True
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run again to update output
|
|
self.buds.run()
|
|
|
|
# Verify: The output correctly displays AMB is enabled
|
|
expected_output = {
|
|
"full_text": "Buds2 LW53 48RW AMB",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
# Action: Toggle AMB again
|
|
self.buds.toggle_amb()
|
|
|
|
# Verify: The correct command is sent to disable AMB this time
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} set ambientsound 0")
|
|
|
|
# Setup: Change the payload one last time to update the AMB status
|
|
modified_payload['payload']['ambient_sound_enabled'] = False
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run again to update output
|
|
self.buds.run()
|
|
|
|
# Verify: The output correctly displays AMB is disabled
|
|
expected_output = {
|
|
"full_text": "Buds2 LW53 48RW",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_toggle_anc(self, mock_run):
|
|
# Setup: ANC is initially disabled
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
modified_payload['payload']['noise_reduction'] = False
|
|
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Toggle ANC
|
|
self.buds.toggle_anc()
|
|
|
|
# Verify: The correct command is sent to enable ANC
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} set anc true")
|
|
|
|
# Setup: Change the payload again to update the ANC status
|
|
modified_payload['payload']['noise_reduction'] = True
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run again to update output
|
|
self.buds.run()
|
|
|
|
# Verify: The output correctly displays ANC is enabled
|
|
expected_output = {
|
|
"full_text": "Buds2 LW53 48RW ANC",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
# Action: Toggle ANC again
|
|
self.buds.toggle_anc()
|
|
|
|
# Verify: The correct command is sent to disable ANC this time
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} set anc false")
|
|
|
|
# Setup: Change the payload one last time to update the ANC status
|
|
modified_payload['payload']['noise_reduction'] = False
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run again to update output
|
|
self.buds.run()
|
|
|
|
# Verify: The output correctly displays ANC is disabled
|
|
expected_output = {
|
|
"full_text": "Buds2 LW53 48RW",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_combined_battery(self, mock_run):
|
|
# Setup: Equal left and right battery value
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
modified_payload['payload']['batt_left'] = modified_payload['payload']['batt_right']
|
|
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() to update the output
|
|
self.buds.run()
|
|
|
|
expected_output = {
|
|
"full_text": "Buds2 LW48RW",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: The output correctly displays combined battery status
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
# Setup: Different left and right battery value
|
|
mock_run.return_value.out = json.dumps(self.payload.get('connected_payload'))
|
|
|
|
# Action: Call run() again to update the output
|
|
self.buds.run()
|
|
|
|
expected_output = {
|
|
"full_text": "Buds2 LW53 48RW",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: The output correctly displays combined battery status
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_combined_battery_drift(self, mock_run):
|
|
# Setup: Different battery level, should show smaller
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
modified_payload['payload']['batt_left'] = modified_payload['payload']['batt_right']
|
|
modified_payload['payload']['batt_left'] -= self.buds.battery_drift_threshold
|
|
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() to update the output
|
|
self.buds.run()
|
|
|
|
expected_level = min(modified_payload['payload']['batt_left'], modified_payload['payload']['batt_right'])
|
|
expected_output = {
|
|
# Verify: The level should be the smallest one
|
|
"full_text":
|
|
f"Buds2 LW{expected_level}RW",
|
|
"color": self.buds.get_gradient(
|
|
expected_level,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: The output correctly displays combined battery status
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
# Setup: One battery is at level 0, should show the other
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
modified_payload['payload']['batt_left'] = 0
|
|
modified_payload['payload']['batt_right'] = 0 + self.buds.battery_drift_threshold
|
|
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() again to update the output
|
|
self.buds.run()
|
|
|
|
expected_level = max(modified_payload['payload']['batt_left'], modified_payload['payload']['batt_right'])
|
|
expected_output = {
|
|
# Verify: The level should be the biggest one
|
|
"full_text":
|
|
f"Buds2 LW{expected_level}RW",
|
|
"color": self.buds.get_gradient(
|
|
expected_level,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: The output correctly displays combined battery status
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_combined_battery_drift_case(self, mock_run):
|
|
# Setup: Change status of one buds to be on the case
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
modified_payload['payload']['placement_left'] = BudsPlacementStatus.case
|
|
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() to update the output
|
|
self.buds.run()
|
|
|
|
expected_output = {
|
|
"full_text": f"Buds2 LC53 48RW 88C",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: The output correctly displays combined battery status
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_connect(self, mock_run):
|
|
# Action: Call connect
|
|
self.buds.connect()
|
|
|
|
# Verify: The correct command is sent to connect
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} connect")
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_disconnect(self, mock_run):
|
|
# Action: Call disconnect
|
|
self.buds.disconnect()
|
|
|
|
# Verify: The correct command is sent to disconnect
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} disconnect")
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_restart_daemin(self, mock_run):
|
|
# Action: Call restart_daemon
|
|
self.buds.restart_daemon()
|
|
|
|
# Verify: The correct command is sent to restart the daemon
|
|
mock_run.assert_called_with(f"{self.buds.earbuds_binary} -kd")
|
|
|
|
def run_placement_helper(self, mock_run, placement_left, placement_right, case_battery, expected_display):
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
modified_payload['payload']['placement_left'] = placement_left
|
|
modified_payload['payload']['placement_right'] = placement_right
|
|
if case_battery is not None:
|
|
modified_payload['payload']['batt_case'] = case_battery
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
self.buds.run()
|
|
|
|
expected_output = {
|
|
"full_text": expected_display,
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_placement_wearing(self, mock_run):
|
|
self.run_placement_helper(
|
|
mock_run,
|
|
BudsPlacementStatus.wearing.value,
|
|
BudsPlacementStatus.wearing.value,
|
|
None,
|
|
"Buds2 LW53 48RW"
|
|
)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_placement_idle(self, mock_run):
|
|
self.run_placement_helper(
|
|
mock_run,
|
|
BudsPlacementStatus.idle.value,
|
|
BudsPlacementStatus.idle.value,
|
|
None,
|
|
"Buds2 LI53 48RI"
|
|
)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_placement_case_with_battery(self, mock_run):
|
|
# Verify: Case battery is returned if a bud is on the case
|
|
self.run_placement_helper(
|
|
mock_run,
|
|
BudsPlacementStatus.case.value,
|
|
BudsPlacementStatus.case.value,
|
|
88,
|
|
"Buds2 LC53 48RC 88C"
|
|
)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_battery_level_dynamic_color(self, mock_run):
|
|
# Setup: Build the colors array independently of our class
|
|
colors = ColorRangeModule.get_hex_color_range(
|
|
self.buds.end_color,
|
|
self.buds.start_color,
|
|
self.buds.battery_limit
|
|
)
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
|
|
for battery_level in range(0, self.buds.battery_limit + 1):
|
|
# Setup: Make both levels equal
|
|
modified_payload['payload']['batt_left'] = battery_level
|
|
modified_payload['payload']['batt_right'] = battery_level
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() again to update the output
|
|
self.buds.run()
|
|
|
|
expected_output = {
|
|
"full_text": f"Buds2 LW{battery_level}RW",
|
|
"color": self.buds.get_gradient(
|
|
battery_level,
|
|
colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_set_equalizer_direct(self, mock_run):
|
|
for eq_setting in BudsEqualizer:
|
|
with self.subTest(msg=f"Failed testing equalizer {eq_setting.name}", eq_setting=eq_setting):
|
|
# Setup: Create a copy of the payload
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call the set function with each equalizer setting
|
|
self.buds.equalizer_set(eq_setting)
|
|
|
|
expected_command = f"{self.buds.earbuds_binary} set equalizer {eq_setting.name}"
|
|
|
|
# Verify: Correct equalizer command is used
|
|
mock_run.assert_called_with(expected_command)
|
|
|
|
# Setup: Modify payload to verify output
|
|
modified_payload['payload']['equalizer_type'] = eq_setting.value
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() again to update the output
|
|
self.buds.run()
|
|
|
|
expected_equalizer = f" {eq_setting.name.capitalize()}" if eq_setting.name != "off" else ""
|
|
expected_output = {
|
|
"full_text": f"Buds2 LW53 48RW{expected_equalizer}",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: Output was updated with equalizer
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_increment_equalizer(self, mock_run):
|
|
# Setup: Create a copy of the payload
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call the set to increment by one the equalizer setting
|
|
self.buds.equalizer_set(+1)
|
|
|
|
# Verify: Correct equalizer command is used
|
|
expected_command = f"{self.buds.earbuds_binary} set equalizer {BudsEqualizer.bass.name}"
|
|
mock_run.assert_called_with(expected_command)
|
|
|
|
# Setup: Modify payload to verify output
|
|
modified_payload['payload']['equalizer_type'] = BudsEqualizer.bass.value
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() again to update the output
|
|
self.buds.run()
|
|
|
|
expected_equalizer = f" {BudsEqualizer.bass.name.capitalize()}"
|
|
expected_output = {
|
|
"full_text": f"Buds2 LW53 48RW{expected_equalizer}",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: Output was updated with equalizer
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_decrement_equalizer_from_off(self, mock_run):
|
|
# Setup: Create a copy of the payload
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call the set to decrement by one the equalizer setting
|
|
self.buds.equalizer_set(-1)
|
|
|
|
# Verify: Correct equalizer command is used
|
|
expected_command = f"{self.buds.earbuds_binary} set equalizer {BudsEqualizer.treble.name}"
|
|
mock_run.assert_called_with(expected_command)
|
|
|
|
# Setup: Modify payload to verify output
|
|
modified_payload['payload']['equalizer_type'] = BudsEqualizer.treble.value
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() again to update the output
|
|
self.buds.run()
|
|
|
|
expected_equalizer = f" {BudsEqualizer.treble.name.capitalize()}"
|
|
expected_output = {
|
|
"full_text": f"Buds2 LW53 48RW{expected_equalizer}",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: Output was updated with equalizer
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
def run_touchpad_set(self, mock_run, setting_value):
|
|
# Setup: Create a copy of the payload
|
|
modified_payload = deepcopy(self.payload.get('connected_payload'))
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call the set with the appropriate setting
|
|
self.buds.touchpad_set(f'{setting_value}')
|
|
|
|
# Verify: Correct command to disable the touchpad is called
|
|
expected_command = f"{self.buds.earbuds_binary} set touchpad {setting_value}"
|
|
mock_run.assert_called_with(expected_command)
|
|
|
|
# Setup: Modify the payload if we are disabling the touchpad
|
|
if setting_value == 'false':
|
|
modified_payload['payload']['tab_lock_status']['touch_an_hold_on'] = False
|
|
modified_payload['payload']['tab_lock_status']['tap_on'] = False
|
|
mock_run.return_value.out = json.dumps(modified_payload)
|
|
|
|
# Action: Call run() again to update the output
|
|
self.buds.run()
|
|
|
|
# Setup:
|
|
expected_output = {
|
|
"full_text": f"Buds2 LW53 48RW{' TL' if setting_value == 'false' else ''}",
|
|
"color": self.buds.get_gradient(
|
|
48,
|
|
self.buds.colors,
|
|
self.buds.battery_limit
|
|
)
|
|
}
|
|
|
|
# Verify: Output was updated with equalizer
|
|
self.assertEqual(expected_output, self.buds.output)
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_touchpad_disable(self, mock_run):
|
|
self.run_touchpad_set(mock_run, "false")
|
|
|
|
@patch('i3pystatus.buds.run_through_shell')
|
|
def test_touchpad_enable(self, mock_run):
|
|
self.run_touchpad_set(mock_run, "true")
|