aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBasil L. Contovounesios2020-06-11 13:49:31 +0100
committerBasil L. Contovounesios2020-06-18 13:11:17 +0100
commit453d30d92cbf940567869d4705c1fcfe57725825 (patch)
treed79b43fdae2af987c36f018635e0c74bd1f67b53
parent23a148c9506f2a5bce71bd5c8822bb7cde6697e8 (diff)
downloademacs-453d30d92cbf940567869d4705c1fcfe57725825.tar.gz
emacs-453d30d92cbf940567869d4705c1fcfe57725825.zip
Improve battery.el UPower support
For discussion, see the following threads: https://lists.gnu.org/archive/html/emacs-devel/2020-01/msg00843.html https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00042.html https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00282.html * etc/NEWS: Announce that battery-upower is enabled by default. * lisp/battery.el (battery-upower-device): Accept both battery and line power device names, or a list thereof (bug#39491). (battery-upower-line-power-device): Remove user option; superseded by battery-upower-device. (battery-upower-subscribe): New user option. (battery-status-function): Check whether a UPower service is provided without activating it. (display-battery-mode): Subscribe to UPower signals when using battery-upower. (battery-upower): Merge data from multiple power sources. Calculate terse battery status %b based on average battery load percentage rather than coarse and often missing BatteryLevel (bug#39491). Add support for average temperature %d. (battery-upower-dbus-service) (battery-upower-dbus-interface) (battery-upower-dbus-path) (battery-upower-dbus-device-interface) (battery-upower-dbus-device-path) (battery-upower-device-all-properties): Rename to... (battery-upower-service) (battery-upower-interface) (battery-upower-path) (battery-upower-device-interface) (battery-upower-device-path) (battery--upower-device-properties): ...these, respectively. (battery-upower-device-list): Rename to... (battery--upower-devices) ...this. Return a flat list of device names determined by battery-upower-device. (battery-upower-types, battery-upower-states) (battery-upower-device-property, battery-upower-device-autodetect): Remove. (battery--upower-signals): New variable. (battery--upower-signal-handler, battery--upower-props-changed) (battery--upower-unsubscribe, battery--upower-subsribe) (battery--upower-state): New functions. * test/lisp/battery-tests.el (battery-upower-state) (battery-upower-state-unknown): New tests.
-rw-r--r--etc/NEWS10
-rw-r--r--lisp/battery.el294
-rw-r--r--test/lisp/battery-tests.el63
3 files changed, 256 insertions, 111 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 4d730228139..2ddb5fb089d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -464,6 +464,16 @@ have now been removed.
464** Battery 464** Battery
465 465
466--- 466---
467*** UPower is now the default battery status backend when available.
468UPower support via the function 'battery-upower' was added in Emacs
46926.1, but was disabled by default. It is now the default value of
470'battery-status-function' when the system provides a UPower D-Bus
471service. The user options 'battery-upower-device' and
472'battery-upower-subscribe' control which power sources to query and
473whether to respond to status change notifications in addition to
474polling, respectively.
475
476---
467*** A richer syntax can be used to format battery status information. 477*** A richer syntax can be used to format battery status information.
468The user options 'battery-mode-line-format' and 478The user options 'battery-mode-line-format' and
469'battery-echo-area-format' now support the full formatting syntax of 479'battery-echo-area-format' now support the full formatting syntax of
diff --git a/lisp/battery.el b/lisp/battery.el
index f6f70b2f16d..e568ab52460 100644
--- a/lisp/battery.el
+++ b/lisp/battery.el
@@ -44,21 +44,40 @@
44 :group 'hardware) 44 :group 'hardware)
45 45
46(defcustom battery-upower-device nil 46(defcustom battery-upower-device nil
47 "UPower device of the `:battery' type. 47 "Preferred UPower device name(s).
48Use `battery-upower-device-list' to list all available UPower devices. 48When `battery-status-function' is set to `battery-upower', this
49If set to nil, then autodetect `:battery' device." 49user option specifies which power sources to query for status
50information and merge into a single report.
51
52When nil (the default), `battery-upower' queries all present
53battery and line power devices as determined by the UPower
54EnumerateDevices method. A string or a nonempty list of strings
55names particular devices to query instead. UPower battery and
56line power device names typically follow the patterns
57\"battery_BATN\" and \"line_power_ACN\", respectively, with N
58starting at 0 when present. Device names should not include the
59leading D-Bus path \"/org/freedesktop/UPower/devices/\"."
50 :version "28.1" 60 :version "28.1"
51 :type '(choice string (const :tag "Autodetect" nil))) 61 :type '(choice (const :tag "Autodetect all devices" nil)
52 62 (string :tag "Device")
53(defcustom battery-upower-line-power-device nil 63 (repeat :tag "Devices" string)))
54 "UPower device of the `:line-power' type. 64
55Use `battery-upower-device-list' to list all available UPower devices. 65(defcustom battery-upower-subscribe t
56If set to nil, then autodetect `:battery' device." 66 "Whether to subscribe to UPower device change signals.
67When nil, battery status information is polled every
68`battery-update-interval' seconds. When non-nil (the default),
69the battery status is also updated whenever a power source is
70added or removed, or when the system starts or stops running on
71battery power.
72
73This only takes effect when `battery-status-function' is set to
74`battery-upower' before enabling `display-battery-mode'."
57 :version "28.1" 75 :version "28.1"
58 :type '(choice string (const :tag "Autodetect" nil))) 76 :type 'boolean)
59 77
60(defconst battery-upower-dbus-service "org.freedesktop.UPower" 78(defconst battery-upower-service "org.freedesktop.UPower"
61 "Well-known UPower service name for the D-Bus system.") 79 "Well-known name of the UPower D-Bus service.
80See URL `https://upower.freedesktop.org/docs/ref-dbus.html'.")
62 81
63(defun battery--files (dir) 82(defun battery--files (dir)
64 "Return a list of absolute file names in DIR or nil on error. 83 "Return a list of absolute file names in DIR or nil on error.
@@ -74,7 +93,7 @@ Value does not include \".\" or \"..\"."
74 (nreverse dirs))) 93 (nreverse dirs)))
75 94
76(defcustom battery-status-function 95(defcustom battery-status-function
77 (cond ((dbus-ping :system battery-upower-dbus-service) 96 (cond ((member battery-upower-service (dbus-list-activatable-names))
78 #'battery-upower) 97 #'battery-upower)
79 ((and (eq system-type 'gnu/linux) 98 ((and (eq system-type 'gnu/linux)
80 (battery--find-linux-sysfs-batteries)) 99 (battery--find-linux-sysfs-batteries))
@@ -219,11 +238,15 @@ seconds."
219 (setq battery-mode-line-string "") 238 (setq battery-mode-line-string "")
220 (or global-mode-string (setq global-mode-string '(""))) 239 (or global-mode-string (setq global-mode-string '("")))
221 (and battery-update-timer (cancel-timer battery-update-timer)) 240 (and battery-update-timer (cancel-timer battery-update-timer))
241 (battery--upower-unsubscribe)
222 (if (and battery-status-function battery-mode-line-format) 242 (if (and battery-status-function battery-mode-line-format)
223 (if (not display-battery-mode) 243 (if (not display-battery-mode)
224 (setq global-mode-string 244 (setq global-mode-string
225 (delq 'battery-mode-line-string global-mode-string)) 245 (delq 'battery-mode-line-string global-mode-string))
226 (add-to-list 'global-mode-string 'battery-mode-line-string t) 246 (add-to-list 'global-mode-string 'battery-mode-line-string t)
247 (and (eq battery-status-function #'battery-upower)
248 battery-upower-subscribe
249 (battery--upower-subsribe))
227 (setq battery-update-timer (run-at-time nil battery-update-interval 250 (setq battery-update-timer (run-at-time nil battery-update-interval
228 #'battery-update-handler)) 251 #'battery-update-handler))
229 (battery-update)) 252 (battery-update))
@@ -577,123 +600,172 @@ The following %-sequences are provided:
577 (_ "N/A")))))) 600 (_ "N/A"))))))
578 601
579 602
580;;; `upowerd' interface. 603;;; UPower interface.
581(defconst battery-upower-dbus-interface "org.freedesktop.UPower" 604
582 "The interface to UPower. 605(defconst battery-upower-interface "org.freedesktop.UPower"
583See URL `https://upower.freedesktop.org/docs/'.") 606 "Name of the UPower D-Bus interface.
607See URL `https://upower.freedesktop.org/docs/UPower.html'.")
584 608
585(defconst battery-upower-dbus-path "/org/freedesktop/UPower" 609(defconst battery-upower-path "/org/freedesktop/UPower"
586 "D-Bus path to talk to UPower service.") 610 "D-Bus object providing `battery-upower-interface'.")
587 611
588(defconst battery-upower-dbus-device-interface 612(defconst battery-upower-device-interface "org.freedesktop.UPower.Device"
589 (concat battery-upower-dbus-interface ".Device") 613 "Name of the UPower Device D-Bus interface.
590 "The Device interface of the UPower.
591See URL `https://upower.freedesktop.org/docs/Device.html'.") 614See URL `https://upower.freedesktop.org/docs/Device.html'.")
592 615
593(defconst battery-upower-dbus-device-path 616(defconst battery-upower-device-path "/org/freedesktop/UPower/devices"
594 (concat battery-upower-dbus-path "/devices") 617 "D-Bus object providing `battery-upower-device-interface'.")
595 "D-Bus path to talk to devices part of the UPower service.") 618
596 619(defvar battery--upower-signals nil
597(defconst battery-upower-types 620 "Handles for UPower signal subscriptions.")
598 '((0 . :unknown) (1 . :line-power) (2 . :battery) 621
599 (3 . :ups) (4 . :monitor) (5 . :mouse) 622(defun battery--upower-signal-handler (&rest _)
600 (6 . :keyboard) (7 . :pda) (8 . :phone)) 623 "Update battery status on receiving a UPower D-Bus signal."
601 "Type of the device.") 624 (timer-event-handler battery-update-timer))
602 625
603(defconst battery-upower-states 626(defun battery--upower-props-changed (_interface changed _invalidated)
604 '((0 . "unknown") (1 . "charging") (2 . "discharging") 627 "Update status when system starts/stops running on battery.
605 (3 . "empty") (4 . "fully-charged") (5 . "pending-charge") 628Intended as a UPower PropertiesChanged signal handler."
606 (6 . "pending-discharge")) 629 (when (assoc "OnBattery" changed)
607 "Alist of battery power states. 630 (battery--upower-signal-handler)))
608Only valid for `:battery' devices.") 631
609 632(defun battery--upower-unsubscribe ()
610(defun battery-upower-device-property (device property) 633 "Unsubscribe from UPower device change signals."
611 "Get value of the single PROPERTY for the UPower DEVICE." 634 (mapc #'dbus-unregister-object battery--upower-signals)
612 (dbus-get-property 635 (setq battery--upower-signals ()))
613 :system battery-upower-dbus-service 636
614 (expand-file-name device battery-upower-dbus-device-path) 637(defun battery--upower-subsribe ()
615 battery-upower-dbus-device-interface 638 "Subscribe to UPower device change signals."
616 property)) 639 (push (dbus-register-signal :system battery-upower-service
617 640 battery-upower-path
618(defun battery-upower-device-all-properties (device) 641 dbus-interface-properties
642 "PropertiesChanged"
643 #'battery--upower-props-changed)
644 battery--upower-signals)
645 (dolist (method '("DeviceAdded" "DeviceRemoved"))
646 (push (dbus-register-signal :system battery-upower-service
647 battery-upower-path
648 battery-upower-interface
649 method #'battery--upower-signal-handler)
650 battery--upower-signals)))
651
652(defun battery--upower-device-properties (device)
619 "Return value for all available properties for the UPower DEVICE." 653 "Return value for all available properties for the UPower DEVICE."
620 (dbus-get-all-properties 654 (dbus-get-all-properties
621 :system battery-upower-dbus-service 655 :system battery-upower-service
622 (expand-file-name device battery-upower-dbus-device-path) 656 (expand-file-name device battery-upower-device-path)
623 battery-upower-dbus-device-interface)) 657 battery-upower-device-interface))
624 658
625(defun battery-upower-device-list () 659(defun battery--upower-devices ()
626 "Return list of all available UPower devices. 660 "List all UPower devices according to `battery-upower-device'."
627Each element is the cons cell in form: (DEVICE . DEVICE-TYPE)." 661 (cond ((stringp battery-upower-device)
628 (mapcar (lambda (device-path) 662 (list battery-upower-device))
629 (let* ((device (file-relative-name 663 (battery-upower-device)
630 device-path battery-upower-dbus-device-path)) 664 ((dbus-call-method :system battery-upower-service
631 (type-num (battery-upower-device-property device "Type"))) 665 battery-upower-path
632 (cons device (or (cdr (assq type-num battery-upower-types)) 666 battery-upower-interface
633 :unknown)))) 667 "EnumerateDevices"))))
634 (dbus-call-method :system battery-upower-dbus-service 668
635 battery-upower-dbus-path 669(defun battery--upower-state (props state)
636 battery-upower-dbus-interface 670 "Merge the UPower battery state in PROPS with STATE.
637 "EnumerateDevices"))) 671This is an extension of the UPower DisplayDevice algorithm for
638 672merging multiple battery states into one. PROPS is an alist of
639(defun battery-upower-device-autodetect (device-type) 673battery properties from `battery-upower-device-interface', and
640 "Return first matching UPower device of DEVICE-TYPE." 674STATE is a symbol representing the state to merge with."
641 (car (rassq device-type (battery-upower-device-list)))) 675 ;; Map UPower enum into our printable symbols.
676 (let* ((new (pcase (cdr (assoc "State" props))
677 (1 'charging)
678 (2 'discharging)
679 (3 'empty)
680 (4 'fully-charged)
681 (5 'pending-charge)
682 (6 'pending-discharge)))
683 ;; Unknown state represented by nil.
684 (either (delq nil (list new state))))
685 ;; Earlier states override later ones.
686 (car (cond ((memq 'charging either))
687 ((memq 'discharging either))
688 ((memq 'pending-charge either))
689 ((memq 'pending-discharge either))
690 ;; Only options left are full or empty,
691 ;; but if they conflict return nil.
692 ((null (cdr either)) either)
693 ((apply #'eq either) either)))))
642 694
643(defun battery-upower () 695(defun battery-upower ()
644 "Get battery status from dbus Upower interface. 696 "Get battery status from UPower D-Bus interface.
645This function works only in systems with `upowerd' daemon 697This function works only in systems that provide a UPower D-Bus
646running. 698service.
647 699
648The following %-sequences are provided: 700The following %-sequences are provided:
649%c Current capacity (mWh) 701%c Current capacity (mWh)
650%p Battery load percentage 702%r Current rate of charge or discharge
651%r Current rate 703%L AC line status (verbose)
652%B Battery status (verbose) 704%B Battery status (verbose)
653%b Battery status: empty means high, `-' means low, 705%b Battery status: empty means high, `-' means low,
654 `!' means critical, and `+' means charging 706 `!' means critical, and `+' means charging
655%L AC line status (verbose) 707%d Temperature (in degrees Celsius)
708%p Battery load percentage
656%s Remaining time (to charge or discharge) in seconds 709%s Remaining time (to charge or discharge) in seconds
657%m Remaining time (to charge or discharge) in minutes 710%m Remaining time (to charge or discharge) in minutes
658%h Remaining time (to charge or discharge) in hours 711%h Remaining time (to charge or discharge) in hours
659%t Remaining time (to charge or discharge) in the form `h:min'" 712%t Remaining time (to charge or discharge) in the form `h:min'"
660 (let* ((bat-device (or battery-upower-device 713 (let ((count 0) props type line-status state load temperature
661 (battery-upower-device-autodetect :battery))) 714 secs mins hrs total-energy total-rate total-tte total-ttf)
662 (bat-props (when bat-device 715 ;; Merge information from all available or specified UPower
663 (battery-upower-device-all-properties bat-device))) 716 ;; devices like other `battery-status-function's.
664 (percents (cdr (assoc "Percentage" bat-props))) 717 (dolist (device (battery--upower-devices))
665 (time-to-empty (cdr (assoc "TimeToEmpty" bat-props))) 718 (setq props (battery--upower-device-properties device))
666 (time-to-full (cdr (assoc "TimeToFull" bat-props))) 719 (setq type (cdr (assoc "Type" props)))
667 (state (cdr (assoc "State" bat-props))) 720 (cond
668 (level (cdr (assoc "BatteryLevel" bat-props))) 721 ((and (eq type 1) (not (eq line-status 'online)))
669 (energy (cdr (assoc "Energy" bat-props))) 722 ;; It's a line power device: `online' if currently providing
670 (energy-rate (cdr (assoc "EnergyRate" bat-props))) 723 ;; power, any other non-nil value if simply present.
671 (lp-device (or battery-upower-line-power-device 724 (setq line-status (if (cdr (assoc "Online" props)) 'online t)))
672 (battery-upower-device-autodetect :line-power))) 725 ((and (eq type 2) (cdr (assoc "IsPresent" props)))
673 (online-p (when lp-device 726 ;; It's a battery.
674 (battery-upower-device-property lp-device "Online"))) 727 (setq count (1+ count))
675 (seconds (if online-p time-to-full time-to-empty)) 728 (setq state (battery--upower-state props state))
676 (minutes (when seconds (/ seconds 60))) 729 (let ((energy (cdr (assoc "Energy" props)))
677 (hours (when minutes (/ minutes 60))) 730 (rate (cdr (assoc "EnergyRate" props)))
678 (remaining-time (when hours 731 (percent (cdr (assoc "Percentage" props)))
679 (format "%d:%02d" hours (mod minutes 60))))) 732 (temp (cdr (assoc "Temperature" props)))
680 (list (cons ?c (if energy (number-to-string (round (* 1000 energy))) "N/A")) 733 (tte (cdr (assoc "TimeToEmpty" props)))
681 (cons ?p (if percents (number-to-string (round percents)) "N/A")) 734 (ttf (cdr (assoc "TimeToFull" props))))
682 (cons ?r (if energy-rate 735 (when energy (setq total-energy (+ (or total-energy 0) energy)))
683 (concat (number-to-string energy-rate) " W") 736 (when rate (setq total-rate (+ (or total-rate 0) rate)))
737 (when percent (setq load (+ (or load 0) percent)))
738 (when temp (setq temperature (+ (or temperature 0) temp)))
739 (when tte (setq total-tte (+ (or total-tte 0) tte)))
740 (when ttf (setq total-ttf (+ (or total-ttf 0) ttf)))))))
741 (when (> count 1)
742 ;; Averages over multiple batteries.
743 (when load (setq load (/ load count)))
744 (when temperature (setq temperature (/ temperature count))))
745 (when (setq secs (if (eq line-status 'online) total-ttf total-tte))
746 (setq mins (/ secs 60))
747 (setq hrs (/ secs 3600)))
748 (list (cons ?c (if total-energy
749 (format "%.0f" (* total-energy 1000))
684 "N/A")) 750 "N/A"))
685 (cons ?B (if state 751 (cons ?r (if total-rate (format "%.1f W" total-rate) "N/A"))
686 (cdr (assq state battery-upower-states)) 752 (cons ?L (cond ((eq line-status 'online) "on-line")
687 "unknown")) 753 (line-status "off-line")
688 (cons ?b (cond ((= level 3) "-") 754 ("N/A")))
689 ((= level 4) "!") 755 (cons ?B (format "%s" (or state 'unknown)))
690 (online-p "+") 756 (cons ?b (cond ((eq state 'charging) "+")
691 (t ""))) 757 ((and load (< load battery-load-critical)) "!")
692 (cons ?L (if online-p "on-line" (if lp-device "off-line" "unknown"))) 758 ((and load (< load battery-load-low)) "-")
693 (cons ?s (if seconds (number-to-string seconds) "N/A")) 759 ("")))
694 (cons ?m (if minutes (number-to-string minutes) "N/A")) 760 ;; Zero usually means unknown.
695 (cons ?h (if hours (number-to-string hours) "N/A")) 761 (cons ?d (if (and temperature (/= temperature 0))
696 (cons ?t (or remaining-time "N/A"))))) 762 (format "%.0f" temperature)
763 "N/A"))
764 (cons ?p (if load (format "%.0f" load) "N/A"))
765 (cons ?s (if secs (number-to-string secs) "N/A"))
766 (cons ?m (if mins (number-to-string mins) "N/A"))
767 (cons ?h (if hrs (number-to-string hrs) "N/A"))
768 (cons ?t (if hrs (format "%d:%02d" hrs (% mins 60)) "N/A")))))
697 769
698 770
699;;; `apm' interface for BSD. 771;;; `apm' interface for BSD.
diff --git a/test/lisp/battery-tests.el b/test/lisp/battery-tests.el
index 92ab013f040..8d7cc7fccf3 100644
--- a/test/lisp/battery-tests.el
+++ b/test/lisp/battery-tests.el
@@ -81,6 +81,69 @@
81 (should (equal (match-string 2 str) "mWh"))) 81 (should (equal (match-string 2 str) "mWh")))
82 (should-not (string-match (rx battery--acpi-capacity eos) "45 mW"))) 82 (should-not (string-match (rx battery--acpi-capacity eos) "45 mW")))
83 83
84(ert-deftest battery-upower-state ()
85 "Test `battery--upower-state'."
86 ;; Charging.
87 (dolist (total '(nil charging discharging empty fully-charged
88 pending-charge pending-discharge))
89 (should (eq (battery--upower-state '(("State" . 1)) total) 'charging)))
90 (dolist (state '(nil 0 1 2 3 4 5 6))
91 (should (eq (battery--upower-state `(("State" . ,state)) 'charging)
92 'charging)))
93 ;; Discharging.
94 (dolist (total '(nil discharging empty fully-charged
95 pending-charge pending-discharge))
96 (should (eq (battery--upower-state '(("State" . 2)) total) 'discharging)))
97 (dolist (state '(nil 0 2 3 4 5 6))
98 (should (eq (battery--upower-state `(("State" . ,state)) 'discharging)
99 'discharging)))
100 ;; Pending charge.
101 (dolist (total '(nil empty fully-charged pending-charge pending-discharge))
102 (should (eq (battery--upower-state '(("State" . 5)) total)
103 'pending-charge)))
104 (dolist (state '(nil 0 3 4 5 6))
105 (should (eq (battery--upower-state `(("State" . ,state)) 'pending-charge)
106 'pending-charge)))
107 ;; Pending discharge.
108 (dolist (total '(nil empty fully-charged pending-discharge))
109 (should (eq (battery--upower-state '(("State" . 6)) total)
110 'pending-discharge)))
111 (dolist (state '(nil 0 3 4 6))
112 (should (eq (battery--upower-state `(("State" . ,state)) 'pending-discharge)
113 'pending-discharge)))
114 ;; Empty.
115 (dolist (total '(nil empty))
116 (should (eq (battery--upower-state '(("State" . 3)) total) 'empty)))
117 (dolist (state '(nil 0 3))
118 (should (eq (battery--upower-state `(("State" . ,state)) 'empty) 'empty)))
119 ;; Fully charged.
120 (dolist (total '(nil fully-charged))
121 (should (eq (battery--upower-state '(("State" . 4)) total) 'fully-charged)))
122 (dolist (state '(nil 0 4))
123 (should (eq (battery--upower-state `(("State" . ,state)) 'fully-charged)
124 'fully-charged))))
125
126(ert-deftest battery-upower-state-unknown ()
127 "Test `battery--upower-state' with unknown states."
128 ;; Unknown running total retains new state.
129 (should-not (battery--upower-state () nil))
130 (should-not (battery--upower-state '(("State" . state)) nil))
131 (should-not (battery--upower-state '(("State" . 0)) nil))
132 (should (eq (battery--upower-state '(("State" . 1)) nil) 'charging))
133 (should (eq (battery--upower-state '(("State" . 2)) nil) 'discharging))
134 (should (eq (battery--upower-state '(("State" . 3)) nil) 'empty))
135 (should (eq (battery--upower-state '(("State" . 4)) nil) 'fully-charged))
136 (should (eq (battery--upower-state '(("State" . 5)) nil) 'pending-charge))
137 (should (eq (battery--upower-state '(("State" . 6)) nil) 'pending-discharge))
138 ;; Unknown new state retains running total.
139 (dolist (props '(() (("State" . state)) (("State" . 0))))
140 (dolist (total '(nil charging discharging empty fully-charged
141 pending-charge pending-discharge))
142 (should (eq (battery--upower-state props total) total))))
143 ;; Conflicting empty and fully-charged.
144 (should-not (battery--upower-state '(("State" . 3)) 'fully-charged))
145 (should-not (battery--upower-state '(("State" . 4)) 'empty)))
146
84(ert-deftest battery-format () 147(ert-deftest battery-format ()
85 "Test `battery-format'." 148 "Test `battery-format'."
86 (should (equal (battery-format "" ()) "")) 149 (should (equal (battery-format "" ()) ""))