#!/usr/bin/env python3 # # vim: set expandtab shiftwidth=4 tabstop=4: # # This file is part of libratbag. # # Copyright 2017 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice (including the next # paragraph) shall be included in all copies or substantial portions of the # Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. import argparse import io import os import resource import subprocess import sys import toolbox import unittest run_ratbagctl_in_subprocess = False class TestRatbagCtl(unittest.TestCase): @classmethod def setUpClass(cls): super(TestRatbagCtl, cls).setUpClass() cls.reset_test_device() @classmethod def run_ratbagctl_subprocess(cls, params): ratbagctl = subprocess.Popen(" ".join([toolbox.RATBAGCTL_PATH, params]), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env={"RATBAGCTL_DEVEL": "org.freedesktop.ratbag_devel1_@ratbagd_sha@"}) (stdout, stderr) = ratbagctl.communicate() return (ratbagctl.returncode, stdout.decode('utf-8').rstrip('\n'), stderr.decode('utf-8').rstrip('\n')) @classmethod def run_ratbagctl_fast(cls, params): global parser, ratbagd stdout = sys.stdout stderr = sys.stderr returncode = 0 sys.stdout = io.StringIO() sys.stderr = io.StringIO() try: cmd = parser.parse(params.split()) cmd.func(ratbagd, cmd) except SystemExit as e: returncode = e.code output = sys.stdout.getvalue() error = sys.stderr.getvalue() sys.stdout = stdout sys.stderr = stderr toolbox.sync_dbus() return returncode, output.rstrip('\n'), error.rstrip('\n') @classmethod def run_ratbagctl(cls, params): if run_ratbagctl_in_subprocess: return cls.run_ratbagctl_subprocess(params) return cls.run_ratbagctl_fast(params) @classmethod def reset_test_device(cls): global ratbagd ratbagd._dbus_call("ResetTestDevice", "") def launch_good_test(self, params): returncode, stdout, stderr = self.run_ratbagctl(params) self.assertEqual(returncode, 0) return stdout def launch_fail_test(self, params): returncode, stdout, stderr = self.run_ratbagctl(params) self.assertNotEqual(returncode, 0) return stdout def launch_fail_with_exception_test(self, params, exception): with self.assertRaises(exception): returncode, stdout, stderr = self.run_ratbagctl(params) return '' @classmethod def setProfile(cls, profile): cls.run_ratbagctl("Profile active set {} test_device".format(profile)) class TestRatbagCtlList(TestRatbagCtl): def test_list(self): r = self.launch_good_test("list") self.assertIn('test_device', r) self.launch_fail_test("list test_device") class TestRatbagCtlInfo(TestRatbagCtl): def test_info(self): self.launch_good_test("info test_device") self.launch_fail_test("info test_device X") self.launch_fail_test("info X") class TestRatbagCtlName(TestRatbagCtl): def test_get(self): r = self.launch_good_test("name test_device") self.assertEqual(r, 'Test device') self.launch_fail_test("name test_device X") self.launch_fail_test("name X") class TestRatbagCtlProfile(TestRatbagCtl): @classmethod def setUpClass(cls): super(TestRatbagCtlProfile, cls).setUpClass() cls.setProfile(0) cls.setProfile(1) cls.setProfile(0) def test_profile_name_get(self): command = "profile 0 name get" r = self.launch_good_test(command + " test_device") self.assertEqual(r, 'Profile 1') self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test("profile name get test_device") def test_profile_name_set(self): r = self.launch_good_test("profile 1 name get test_device") self.assertEqual(r, 'Profile 2') self.launch_good_test("profile 1 name set banana test_device") r = self.launch_good_test("profile 1 name get test_device") self.assertEqual(r, 'banana') # profile 0 doesn't have the capability RATBAG_PROFILE_CAP_WRITABLE_NAME self.launch_fail_with_exception_test("profile 0 name set kiwi test_device", toolbox.RatbagErrorCapability) # better be safe than sorry, checking that the previous actually failed :) r = self.launch_good_test("profile 0 name get test_device") self.assertNotEqual(r, 'kiwi') self.launch_fail_test("profile 1 name set blah") self.launch_fail_test("profile 1 name set blah X test_device") self.launch_fail_test("profile name set blah test_device") def test_profile_active_get(self): command = "profile active get" r = self.launch_good_test(command + " test_device") self.assertEqual(int(r), 0) self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") def test_profile_active_set(self): command = "profile active set" r = self.launch_good_test(command + " 1 test_device") self.assertEqual(r, '') r = self.launch_good_test("profile active get test_device") self.assertEqual(int(r), 1) self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 1 test_device X") def test_profile_n(self): r0 = self.launch_good_test("Profile 0 get test_device") r1 = self.launch_good_test("Profile 1 get test_device") self.assertNotEqual(r0, r1) self.launch_fail_test("Profile 0 get") self.launch_fail_test("Profile 0 test_device") self.launch_fail_test("Profile 0 get test_device X") self.launch_fail_test("Profile 10 get test_device") def test_profile_enable(self): r = self.launch_good_test("profile 0 get test_device") self.assertNotIn('disabled', r) r = self.launch_good_test("profile 3 get test_device") self.assertIn('disabled', r) self.launch_good_test("profile 3 enable test_device") r = self.launch_good_test("profile 3 get test_device") self.assertNotIn('Profile 3: (disabled)', r) self.launch_fail_test("profile enable") self.launch_fail_test("profile X enable test_device") self.launch_fail_test("profile 1 enable test_device X") self.launch_fail_test("profile 10 enable test_device") def test_profile_disable(self): r = self.launch_good_test("profile 2 get test_device") self.assertNotIn('disabled', r) self.launch_good_test("profile 2 disable test_device") r = self.launch_good_test("profile 2 get test_device") self.assertEqual('Profile 2: (disabled)', r) self.launch_fail_test("profile disable") self.launch_fail_test("profile X disable test_device") self.launch_fail_test("profile 1 disable test_device X") self.launch_fail_test("profile 10 disable test_device") class TestRatbagCtlResolution(TestRatbagCtl): @classmethod def setUpClass(cls): super(TestRatbagCtlResolution, cls).setUpClass() cls.setProfile(1) def test_resolution_active_get(self): command = "Resolution active get" r = self.launch_good_test(command + " test_device") self.assertEqual(int(r), 2) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 10 test_device") self.launch_fail_test(command + " test_device x") def test_resolution_active_set(self): command = "Resolution active set" r = self.launch_good_test("Resolution active get test_device") self.assertEqual(int(r), 2) r = self.launch_good_test(command + " 0 test_device") self.assertEqual(r, '') r = self.launch_good_test("Resolution active get test_device") self.assertEqual(int(r), 0) self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 1 test_device X") def test_resolution_n_get(self): r = self.launch_good_test("Resolution 1 get test_device") self.assertEqual(r, "1: 1200x1300dpi @ 2000Hz (default)") self.launch_fail_test("Resolution 10 get test_device") self.launch_fail_test("Resolution 1 get test_device X") self.launch_fail_test("Resolution 1 get X") def test_profile_resolution(self): r = self.launch_good_test("Profile 2 Resolution 2 get test_device") self.assertEqual(r, "2: 2300x2400dpi @ 3000Hz") def test_resolution_default_get(self): command = "Resolution default get" r = self.launch_good_test(command + " test_device") self.assertEqual(int(r), 1) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 10 test_device") self.launch_fail_test(command + " test_device x") def test_resolution_default_set(self): command = "Resolution default set" self.launch_good_test(command + " 2 test_device") r = self.launch_good_test("Resolution default get test_device") self.assertEqual(int(r), 2) self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 1 test_device X") # Reset for other tests self.launch_good_test(command + " 1 test_device") class TestRatbagCtlDPI(TestRatbagCtl): @classmethod def setUpClass(cls): super(TestRatbagCtlDPI, cls).setUpClass() cls.setProfile(0) def test_dpi_get(self): command = "DPI get" r = self.launch_good_test(command + " test_device") self.assertEqual(int(r), 200) self.setProfile(1) r = self.launch_good_test(command + " test_device") self.assertEqual(r, "1300x1400dpi") self.launch_fail_test(command) self.launch_fail_test(command + " 1 test_device") self.launch_fail_test(command + " test_device X") def test_dpi_set(self): command = "DPI set" self.setProfile(0) r = self.launch_good_test("DPI get test_device") res = int(r) + 50 r = self.launch_good_test(command + " {} test_device".format(res)) self.assertEqual(r, '') r = self.launch_good_test("DPI get test_device") self.assertEqual(int(r), res) self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 100 test_device X") def test_dpi_set_xy(self): command = "DPI set" self.setProfile(2) r = self.launch_good_test("DPI get test_device") self.assertEqual(r, "2100x2200dpi") self.launch_good_test(command + " 1350x1450 test_device") r = self.launch_good_test("DPI get test_device") self.assertEqual(r, "1350x1450dpi") self.launch_good_test(command + " 2350x2450dpi test_device") r = self.launch_good_test("DPI get test_device") self.assertEqual(r, "2350x2450dpi") self.launch_good_test(command + " 2350 test_device") r = self.launch_good_test("DPI get test_device") self.assertEqual(r, "2350x2350dpi") self.launch_fail_test(command + " 2350dpi test_device") self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 100 test_device X") def test_prefix_dpi(self): self.setProfile(0) r = self.launch_good_test("Profile 1 DPI get test_device") self.assertEqual(r, "1300x1400dpi") r = self.launch_good_test("Profile 1 Resolution 1 DPI get test_device") self.assertEqual(r, "1200x1300dpi") r = self.launch_good_test("Resolution 2 DPI get test_device") self.assertEqual(int(r), 300) self.launch_fail_test("Profile 1 Profile 1 DPI get test_device") self.launch_fail_test("Profile 1 Resolution 1 Resolution 2 DPI get test_device") class TestRatbagCtlReportRate(TestRatbagCtl): @classmethod def setUpClass(cls): super(TestRatbagCtlReportRate, cls).setUpClass() cls.setProfile(0) def test_rate_get(self): command = "rate get" r = self.launch_good_test(command + " test_device") self.assertEqual(int(r), 1000) self.launch_fail_test(command) self.launch_fail_test(command + " 1 test_device") self.launch_fail_test(command + " test_device X") def test_rate_set(self): command = "rate set" r = self.launch_good_test("rate get test_device") res = int(r) + 500 r = self.launch_good_test(command + " {} test_device".format(res)) self.assertEqual(r, '') r = self.launch_good_test("rate get test_device") self.assertEqual(int(r), res) self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 100 test_device X") def test_prefix_rate(self): r = self.launch_good_test("Profile 1 rate get test_device") self.assertEqual(int(r), 2000) r = self.launch_good_test("Profile 1 Resolution 1 rate get test_device") self.assertEqual(int(r), 2000) r = self.launch_good_test("Resolution 2 rate get test_device") self.assertEqual(int(r), 1000) self.launch_fail_test("Profile 1 Profile 1 rate get test_device") self.launch_fail_test("Profile 1 Resolution 1 Resolution 2 rate get test_device") class TestRatbagCtlButton(TestRatbagCtl): @classmethod def setUpClass(cls): super(TestRatbagCtlButton, cls).setUpClass() cls.setProfile(0) def test_button_count(self): command = "button count" r = self.launch_good_test(command + " test_device") self.assertEqual(int(r), 4) self.launch_fail_test(command) self.launch_fail_test(command + " 1 test_device") self.launch_fail_test(command + " test_device X") def test_button_n_get(self): self.setProfile(0) r = self.launch_good_test("button 0 get test_device") self.assertEqual(r, "Button: 0 type left is mapped to 'button 0'") r = self.launch_good_test("button 1 get test_device") self.assertEqual(r, "Button: 1 type middle is mapped to UNKNOWN") r = self.launch_good_test("button 2 get test_device") self.assertEqual(r, "Button: 2 type right is mapped to 'profile-cycle-up'") r = self.launch_good_test("button 3 get test_device") self.assertEqual(r, "Button: 3 type unknown is mapped to macro '↕B 300ms'") self.launch_fail_test("button 1 get 10 test_device") self.launch_fail_test("button 10 get test_device") self.launch_fail_test("button 1 get test_device X") def test_button_n_action_get(self): self.setProfile(0) r = self.launch_good_test("button 0 action get test_device") self.assertEqual(r, "Button: 0 type left is mapped to 'button 0'") self.launch_fail_test("button 1 action get 10 test_device") self.launch_fail_test("button 10 action get test_device") self.launch_fail_test("button 1 action get test_device X") def test_button_n_action_set_button(self): self.setProfile(2) command = "button 2 action set button" r = self.launch_good_test("button 2 action get test_device") self.assertEqual(r, "Button: 2 type right is mapped to 'button 2'") r = self.launch_good_test(command + " 1 test_device") self.assertEqual(r, '') r = self.launch_good_test("button 2 action get test_device") self.assertEqual(r, "Button: 2 type right is mapped to 'button 1'") self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 100 test_device X") def test_button_n_action_set_special(self): self.setProfile(2) command = "button 0 action set special " r = self.launch_good_test("button 0 action get test_device") self.assertEqual(r, "Button: 0 type left is mapped to 'profile-cycle-up'") r = self.launch_good_test(command + " second-mode test_device") self.assertEqual(r, '') r = self.launch_good_test("button 0 action get test_device") self.assertEqual(r, "Button: 0 type left is mapped to 'second-mode'") self.launch_fail_test(command) self.launch_fail_test(command + " X test_device") self.launch_fail_test(command + " 100 test_device X") def test_button_n_action_set_macro(self): command = "button 2 action set macro " self.launch_good_test(command + '+KEY_B KEY_A -KEY_B KEY_C test_device') r = self.launch_good_test("button 2 get test_device") self.assertEqual(r, "Button: 2 type right is mapped to macro '↓B ↕A ↑B ↕C'") self.launch_good_test(command + 't100 t200 test_device') r = self.launch_good_test("button 2 get test_device") self.assertEqual(r, "Button: 2 type right is mapped to macro '100ms 200ms'") self.launch_good_test(command + 'KeY_z test_device') r = self.launch_good_test("button 2 get test_device") self.assertEqual(r, "Button: 2 type right is mapped to macro '↕Z'") self.launch_good_test(command + ' test_device') r = self.launch_good_test("button 2 get test_device") self.assertEqual(r, "Button: 2 type right is mapped to macro 'None'") self.launch_fail_test(command) self.launch_fail_test(command + " KEY_A test_device X") def test_prefix_button(self): r = self.launch_good_test("Profile 2 Button 3 get test_device") self.assertEqual(r, "Button: 3 type unknown is mapped to 'button 3'") self.launch_fail_test("Profile 2 Profile 2 Button 3 get test_device") self.launch_fail_test("Profile 2 Resolution 2 Button 3 get test_device") class TestRatbagCtlLED(TestRatbagCtl): @classmethod def setUpClass(cls): super(TestRatbagCtlLED, cls).setUpClass() cls.setProfile(0) def test_led_n_get(self): self.setProfile(0) r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: off") r = self.launch_good_test("led 1 get test_device") self.assertEqual(r, "LED: 1 type: side, depth: rgb, mode: on, color: ff0000") r = self.launch_good_test("led 2 get test_device") self.assertEqual(r, "LED: 2 type: side, depth: rgb, mode: cycle, rate: 3, brightness: 40") self.launch_fail_test("led 0 get 10 test_device") self.launch_fail_test("led 10 get test_device") self.launch_fail_test("led 0 get test_device X") def test_led_n_set_mode(self): self.setProfile(0) r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: off") r = self.launch_good_test("led 0 set mode cycle test_device") self.assertEqual(r, '') r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: cycle, rate: 1, brightness: 20") self.launch_fail_test("led 0 set mode bicycle test_device") self.launch_fail_test("led 0 set mode test_device") self.launch_fail_test("led 0 set mode cycle test_device X") self.launch_fail_test("led 0 set mode cycle X test_device") def test_led_n_set_color(self): self.setProfile(0) r = self.launch_good_test("led 1 get test_device") self.assertEqual(r, "LED: 1 type: side, depth: rgb, mode: on, color: ff0000") r = self.launch_good_test("led 1 set color 00ff00 test_device") self.assertEqual(r, '') r = self.launch_good_test("led 1 get test_device") self.assertEqual(r, "LED: 1 type: side, depth: rgb, mode: on, color: 00ff00") r = self.launch_good_test("led 1 set color 0x0000ff test_device") self.assertEqual(r, '') r = self.launch_good_test("led 1 get test_device") self.assertEqual(r, "LED: 1 type: side, depth: rgb, mode: on, color: 0000ff") self.launch_fail_test("led 1 set color g0ff00 test_device") self.launch_fail_test("led 1 set color test_device") self.launch_fail_test("led 1 set color 00ff00 test_device X") self.launch_fail_test("led 1 set color 00ff00 X test_device") def test_led_n_set_rate(self): self.setProfile(0) r = self.launch_good_test("led 2 get test_device") self.assertEqual(r, "LED: 2 type: side, depth: rgb, mode: cycle, rate: 3, brightness: 40") r = self.launch_good_test("led 2 set rate 100 test_device") self.assertEqual(r, '') r = self.launch_good_test("led 2 get test_device") self.assertEqual(r, "LED: 2 type: side, depth: rgb, mode: cycle, rate: 100, brightness: 40") self.launch_fail_test("led 2 set rate this_is_not_an_int test_device") self.launch_fail_test("led 2 set rate test_device") self.launch_fail_test("led 2 set rate 100 test_device X") self.launch_fail_test("led 2 set rate 100 X test_device") def test_led_n_set_brightness(self): self.setProfile(2) r = self.launch_good_test("led 1 get test_device") self.assertEqual(r, "LED: 1 type: side, depth: rgb, mode: cycle, rate: 3, brightness: 40") r = self.launch_good_test("led 1 set brightness 100 test_device") self.assertEqual(r, '') r = self.launch_good_test("led 1 get test_device") self.assertEqual(r, "LED: 1 type: side, depth: rgb, mode: cycle, rate: 3, brightness: 100") self.launch_fail_test("led 1 set brightness this_is_not_an_int test_device") self.launch_fail_test("led 1 set brightness test_device") self.launch_fail_test("led 1 set brightness 100 test_device X") self.launch_fail_test("led 1 set brightness 100 X test_device") self.launch_fail_test("led 1 set brightness this_is_not_an_int test_device") self.launch_fail_test("led 1 set brightness 256 test_device") self.launch_fail_test("led 1 set brightness -10 test_device") def test_led_n_set_combination(self): self.setProfile(2) r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: on, color: ff0000") r = self.launch_good_test("led 0 set mode cycle brightness 100 test_device") r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: cycle, rate: 1, brightness: 100") r = self.launch_good_test("led 0 set mode cycle rate 100 brightness 255 test_device") r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: cycle, rate: 100, brightness: 255") r = self.launch_good_test("led 0 set mode on color 00ff00 test_device") r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: on, color: 00ff00") r = self.launch_good_test("led 0 set rate 100 brightness 200 mode cycle test_device") r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: cycle, rate: 100, brightness: 200") r = self.launch_good_test("led 0 set rate 100 brightness 200 mode cycle brightness 100 test_device") r = self.launch_good_test("led 0 get test_device") self.assertEqual(r, "LED: 0 type: logo, depth: rgb, mode: cycle, rate: 100, brightness: 100") self.launch_fail_test("led 0 set brightness 10 mode bicycle test_device") def test_prefix_led(self): self.setProfile(2) r = self.launch_good_test("Profile 1 Led 1 get test_device") self.assertEqual(r, "LED: 1 type: side, depth: rgb, mode: off") self.launch_fail_test("Profile 1 Profile 2 Led 1 get test_device") self.launch_fail_test("Profile 1 Resolution 2 Led 1 get test_device") def setUpModule(): global ratbagd_process, ratbagd, parser ratbagd_process = toolbox.start_ratbagd() assert ratbagd_process is not None ratbagd = toolbox.open_ratbagd(ratbagd_process) assert ratbagd is not None parser = toolbox.get_parser() def tearDownModule(): global ratbagd_process toolbox.terminate_ratbagd(ratbagd_process) def parse(input_string): global run_ratbagctl_in_subprocess parser_test = argparse.ArgumentParser(description="Testsuite for ratbagd/ratbagctl", add_help=False) parser_test.add_argument("--subprocess", action='store_true', default=False) ns, rest = parser_test.parse_known_args(input_string) run_ratbagctl_in_subprocess = ns.subprocess return rest def main(argv): if not os.geteuid() == 0: sys.exit('Script must be run as root') os.environ['RATBAG_TEST'] = '1' resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) args = parse(argv) unittest.main(argv=[sys.argv[0], *args]) if __name__ == "__main__": main(sys.argv[1:])