#!/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()