Files
daily-notification-plugin/scripts/daily-notification-test.py
Matthew Raymer dc4d342bef feat(scripts): add automated testing scripts for notification system
- Add daily-notification-test.sh for basic notification testing
- Add daily-notification-test.py for Python-based testing
- Add reboot-test.sh for automated reboot recovery testing
- Include comprehensive error handling and logging
- Add colored output for better user experience
- Support for different testing scenarios and edge cases
- Include ADB command validation and device connectivity checks

Scripts provide:
- Automated notification scheduling and verification
- Reboot recovery testing with proper timing
- Permission management testing
- Comprehensive logging and error reporting
- Cross-platform compatibility (bash and Python)

These scripts enable automated testing of the complete notification
system including boot receiver and app startup recovery mechanisms.
2025-10-14 06:17:09 +00:00

263 lines
9.6 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
DailyNotification Plugin Automated Test Suite
Usage: python3 daily-notification-test.py [OPTIONS]
"""
import subprocess
import time
import json
import sys
import argparse
from typing import Optional, Dict, Any, List
from dataclasses import dataclass
from enum import Enum
class TestResult(Enum):
PASS = "PASS"
FAIL = "FAIL"
SKIP = "SKIP"
@dataclass
class TestCase:
name: str
description: str
result: TestResult
duration: float
error: Optional[str] = None
class DailyNotificationTester:
def __init__(self, package: str = "com.timesafari.dailynotification", verbose: bool = False):
self.package = package
self.activity = f"{package}/.MainActivity"
self.verbose = verbose
self.test_results: List[TestCase] = []
def log(self, message: str, level: str = "INFO"):
"""Log message with timestamp"""
timestamp = time.strftime("%H:%M:%S")
if level == "ERROR":
print(f"❌ [{timestamp}] {message}")
elif level == "SUCCESS":
print(f"✅ [{timestamp}] {message}")
elif level == "WARNING":
print(f"⚠️ [{timestamp}] {message}")
elif level == "INFO":
print(f" [{timestamp}] {message}")
else:
print(f"[{timestamp}] {message}")
def run_adb_command(self, command: str, capture_output: bool = True) -> subprocess.CompletedProcess:
"""Run ADB command and return result"""
full_command = f"adb {command}"
if self.verbose:
self.log(f"Running: {full_command}")
try:
return subprocess.run(
full_command,
shell=True,
capture_output=capture_output,
text=True,
timeout=30
)
except subprocess.TimeoutExpired:
self.log(f"Command timed out: {full_command}", "ERROR")
raise
def is_app_running(self) -> bool:
"""Check if app is currently running"""
result = self.run_adb_command(f'shell "ps | grep {self.package}"')
return result.returncode == 0 and self.package in result.stdout
def launch_app(self) -> bool:
"""Launch the app"""
self.log("Launching app...")
result = self.run_adb_command(f"shell am start -n {self.activity}")
time.sleep(3) # Wait for app to load
return self.is_app_running()
def send_to_background(self) -> bool:
"""Send app to background"""
self.log("Sending app to background...")
self.run_adb_command("shell input keyevent KEYCODE_HOME")
time.sleep(2)
return self.is_app_running()
def force_stop_app(self) -> bool:
"""Force stop the app"""
self.log("Force stopping app...")
self.run_adb_command(f"shell am force-stop {self.package}")
time.sleep(2)
return not self.is_app_running()
def check_notification_logs(self, timeout: int = 60) -> bool:
"""Check for notification success in logs"""
self.log(f"Checking notification logs (timeout: {timeout}s)...")
start_time = time.time()
while time.time() - start_time < timeout:
result = self.run_adb_command("logcat -d")
if "Notification displayed successfully" in result.stdout:
return True
time.sleep(5)
return False
def check_permissions(self) -> Dict[str, bool]:
"""Check app permissions"""
self.log("Checking app permissions...")
result = self.run_adb_command(f"shell dumpsys package {self.package}")
permissions = {
"notifications": "POST_NOTIFICATIONS" in result.stdout,
"exact_alarm": "SCHEDULE_EXACT_ALARM" in result.stdout,
"wake_lock": "WAKE_LOCK" in result.stdout
}
return permissions
def check_scheduled_alarms(self) -> bool:
"""Check if any alarms are scheduled for the app"""
self.log("Checking scheduled alarms...")
result = self.run_adb_command("shell \"dumpsys alarm | grep timesafari\"")
return result.returncode == 0 and self.package in result.stdout
def run_test(self, test_name: str, test_func, *args, **kwargs) -> TestCase:
"""Run a single test case"""
self.log(f"Running test: {test_name}")
start_time = time.time()
try:
result = test_func(*args, **kwargs)
duration = time.time() - start_time
if result:
self.log(f"Test passed: {test_name}", "SUCCESS")
return TestCase(test_name, "", TestResult.PASS, duration)
else:
self.log(f"Test failed: {test_name}", "ERROR")
return TestCase(test_name, "", TestResult.FAIL, duration, "Test returned False")
except Exception as e:
duration = time.time() - start_time
self.log(f"Test error: {test_name} - {str(e)}", "ERROR")
return TestCase(test_name, "", TestResult.FAIL, duration, str(e))
def run_test_suite(self) -> Dict[str, Any]:
"""Run complete test suite"""
self.log("🧪 Starting DailyNotification Test Suite")
self.log("=" * 50)
# Check ADB connection
result = self.run_adb_command("devices")
if "device" not in result.stdout:
self.log("No Android device connected via ADB", "ERROR")
sys.exit(1)
self.log("ADB device connected", "SUCCESS")
# Test 1: App Launch
test_case = self.run_test("App Launch", self.launch_app)
self.test_results.append(test_case)
# Test 2: Background Test
test_case = self.run_test("Background Operation", self.send_to_background)
self.test_results.append(test_case)
# Test 3: Force Stop Test
test_case = self.run_test("Force Stop", self.force_stop_app)
self.test_results.append(test_case)
# Test 4: Permission Check
def check_permissions_test():
permissions = self.check_permissions()
return any(permissions.values())
test_case = self.run_test("Permission Check", check_permissions_test)
self.test_results.append(test_case)
# Test 5: Alarm Check
test_case = self.run_test("Alarm Scheduling", self.check_scheduled_alarms)
self.test_results.append(test_case)
# Test 6: Notification Test (requires manual scheduling)
self.log("📱 Manual Notification Test")
self.log("⚠️ Manual step required: Schedule notification in app", "WARNING")
input("Press Enter when notification is scheduled...")
test_case = self.run_test("Notification Delivery", self.check_notification_logs, 120)
self.test_results.append(test_case)
return self.generate_report()
def generate_report(self) -> Dict[str, Any]:
"""Generate test report"""
total_tests = len(self.test_results)
passed_tests = sum(1 for test in self.test_results if test.result == TestResult.PASS)
failed_tests = sum(1 for test in self.test_results if test.result == TestResult.FAIL)
report = {
"summary": {
"total": total_tests,
"passed": passed_tests,
"failed": failed_tests,
"success_rate": (passed_tests / total_tests * 100) if total_tests > 0 else 0
},
"tests": [
{
"name": test.name,
"result": test.result.value,
"duration": test.duration,
"error": test.error
}
for test in self.test_results
]
}
return report
def print_report(self, report: Dict[str, Any]):
"""Print test report"""
self.log("\n📊 Test Results Summary:")
self.log("=" * 30)
summary = report["summary"]
self.log(f"Total Tests: {summary['total']}")
self.log(f"Passed: {summary['passed']}")
self.log(f"Failed: {summary['failed']}")
self.log(f"Success Rate: {summary['success_rate']:.1f}%")
self.log("\nDetailed Results:")
for test in report["tests"]:
status = "✅ PASS" if test["result"] == "PASS" else "❌ FAIL"
duration = f"({test['duration']:.1f}s)"
self.log(f"{test['name']}: {status} {duration}")
if test["error"]:
self.log(f" Error: {test['error']}", "ERROR")
def main():
parser = argparse.ArgumentParser(description="DailyNotification Plugin Test Suite")
parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
parser.add_argument("-p", "--package", default="com.timesafari.dailynotification", help="App package name")
parser.add_argument("-o", "--output", help="Output report to JSON file")
parser.add_argument("--timeout", type=int, default=120, help="Test timeout in seconds")
args = parser.parse_args()
tester = DailyNotificationTester(package=args.package, verbose=args.verbose)
report = tester.run_test_suite()
tester.print_report(report)
# Save report to file if specified
if args.output:
with open(args.output, 'w') as f:
json.dump(report, f, indent=2)
tester.log(f"Report saved to: {args.output}")
# Exit with error code if any test failed
if report["summary"]["failed"] > 0:
sys.exit(1)
if __name__ == "__main__":
main()