849 lines
30 KiB
Python
849 lines
30 KiB
Python
"""
|
||
EU-Utility Integration Test - Platform Compatibility
|
||
=====================================================
|
||
|
||
Tests cross-platform compatibility:
|
||
- Windows-specific features
|
||
- Linux compatibility
|
||
- Path handling differences
|
||
- File locking mechanisms
|
||
- Process management
|
||
|
||
Author: Integration Tester
|
||
Version: 1.0.0
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
import platform
|
||
import subprocess
|
||
import threading
|
||
import tempfile
|
||
from pathlib import Path
|
||
from typing import Dict, Any, List, Tuple, Optional
|
||
from dataclasses import dataclass, asdict
|
||
from datetime import datetime
|
||
|
||
from plugins.base_plugin import BasePlugin
|
||
from PyQt6.QtWidgets import (
|
||
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
||
QTextEdit, QTableWidget, QTableWidgetItem, QHeaderView,
|
||
QGroupBox, QProgressBar, QTabWidget
|
||
)
|
||
from PyQt6.QtCore import Qt
|
||
|
||
|
||
@dataclass
|
||
class PlatformTest:
|
||
"""Platform compatibility test case."""
|
||
name: str
|
||
category: str # 'path', 'file', 'process', 'system'
|
||
description: str
|
||
windows_only: bool = False
|
||
linux_only: bool = False
|
||
|
||
|
||
class PlatformCompatibilityTester(BasePlugin):
|
||
"""Plugin for testing cross-platform compatibility."""
|
||
|
||
name = "Platform Compatibility Tester"
|
||
version = "1.0.0"
|
||
author = "Integration Tester"
|
||
description = "Test cross-platform compatibility and platform-specific features"
|
||
|
||
# Platform info
|
||
IS_WINDOWS = sys.platform == 'win32'
|
||
IS_LINUX = sys.platform == 'linux'
|
||
IS_MAC = sys.platform == 'darwin'
|
||
|
||
# Test cases
|
||
TEST_CASES = [
|
||
PlatformTest("Path Separator", "path", "Test path separator handling"),
|
||
PlatformTest("Path Resolution", "path", "Test path resolution"),
|
||
PlatformTest("Unicode Paths", "path", "Test Unicode in paths"),
|
||
PlatformTest("Long Paths", "path", "Test long path handling (Windows)", windows_only=True),
|
||
PlatformTest("UNC Paths", "path", "Test UNC path handling (Windows)", windows_only=True),
|
||
PlatformTest("File Locking - fcntl", "file", "Test fcntl file locking (Linux/Mac)", linux_only=True),
|
||
PlatformTest("File Locking - portalocker", "file", "Test portalocker file locking (Windows)"),
|
||
PlatformTest("File Permissions", "file", "Test file permission handling"),
|
||
PlatformTest("Temp Directory", "file", "Test temp directory access"),
|
||
PlatformTest("Process List", "process", "Test process enumeration"),
|
||
PlatformTest("Window Enumeration", "process", "Test window enumeration (Windows)", windows_only=True),
|
||
PlatformTest("CPU Info", "system", "Test CPU information retrieval"),
|
||
PlatformTest("Memory Info", "system", "Test memory information retrieval"),
|
||
PlatformTest("Environment Variables", "system", "Test environment variable handling"),
|
||
]
|
||
|
||
def initialize(self):
|
||
"""Initialize the tester."""
|
||
self.log_info("Platform Compatibility Tester initialized")
|
||
self._test_results: List[Dict] = []
|
||
|
||
def get_ui(self) -> QWidget:
|
||
"""Create the plugin UI."""
|
||
widget = QWidget()
|
||
layout = QVBoxLayout(widget)
|
||
|
||
# Title
|
||
title = QLabel("Platform Compatibility Tester")
|
||
title.setStyleSheet("font-size: 16px; font-weight: bold;")
|
||
layout.addWidget(title)
|
||
|
||
# Platform info banner
|
||
self._create_platform_banner(layout)
|
||
|
||
# Tabs
|
||
tabs = QTabWidget()
|
||
|
||
# Overview tab
|
||
tabs.addTab(self._create_overview_tab(), "Overview")
|
||
|
||
# Path Tests tab
|
||
tabs.addTab(self._create_path_tab(), "Path Tests")
|
||
|
||
# File Tests tab
|
||
tabs.addTab(self._create_file_tab(), "File Tests")
|
||
|
||
# System Tests tab
|
||
tabs.addTab(self._create_system_tab(), "System Tests")
|
||
|
||
# Test Results tab
|
||
tabs.addTab(self._create_results_tab(), "Test Results")
|
||
|
||
layout.addWidget(tabs)
|
||
|
||
return widget
|
||
|
||
def _create_platform_banner(self, parent_layout):
|
||
"""Create the platform info banner."""
|
||
banner = QGroupBox("Current Platform")
|
||
banner_layout = QHBoxLayout(banner)
|
||
|
||
# Platform icon/color
|
||
if self.IS_WINDOWS:
|
||
platform_text = "🪟 Windows"
|
||
color = "#0078D4"
|
||
elif self.IS_LINUX:
|
||
platform_text = "🐧 Linux"
|
||
color = "#FCC624"
|
||
elif self.IS_MAC:
|
||
platform_text = "🍎 macOS"
|
||
color = "#999999"
|
||
else:
|
||
platform_text = f"❓ {sys.platform}"
|
||
color = "#888888"
|
||
|
||
# Platform label
|
||
platform_label = QLabel(f"Platform: {platform_text}")
|
||
platform_label.setStyleSheet(f"color: {color}; font-weight: bold;")
|
||
banner_layout.addWidget(platform_label)
|
||
|
||
# Python version
|
||
python_label = QLabel(f"Python: {platform.python_version()}")
|
||
banner_layout.addWidget(python_label)
|
||
|
||
# Architecture
|
||
arch_label = QLabel(f"Arch: {platform.machine()}")
|
||
banner_layout.addWidget(arch_label)
|
||
|
||
# Processor info
|
||
proc_label = QLabel(f"Processor: {platform.processor() or 'Unknown'}")
|
||
banner_layout.addWidget(proc_label)
|
||
|
||
banner_layout.addStretch()
|
||
parent_layout.addWidget(banner)
|
||
|
||
def _create_overview_tab(self) -> QWidget:
|
||
"""Create the overview tab."""
|
||
widget = QWidget()
|
||
layout = QVBoxLayout(widget)
|
||
|
||
# Run all tests button
|
||
run_btn = QPushButton("Run All Compatibility Tests")
|
||
run_btn.setStyleSheet("font-size: 14px; padding: 10px;")
|
||
run_btn.clicked.connect(self._run_all_tests)
|
||
layout.addWidget(run_btn)
|
||
|
||
# Platform details
|
||
details = QTextEdit()
|
||
details.setReadOnly(True)
|
||
details.setHtml(self._get_platform_details_html())
|
||
layout.addWidget(details)
|
||
|
||
# Feature support matrix
|
||
layout.addWidget(QLabel("Feature Support Matrix:"))
|
||
matrix = QTextEdit()
|
||
matrix.setReadOnly(True)
|
||
matrix.setMaximumHeight(200)
|
||
matrix.setHtml(self._get_feature_matrix_html())
|
||
layout.addWidget(matrix)
|
||
|
||
return widget
|
||
|
||
def _create_path_tab(self) -> QWidget:
|
||
"""Create the path tests tab."""
|
||
widget = QWidget()
|
||
layout = QVBoxLayout(widget)
|
||
|
||
layout.addWidget(QLabel("Path Handling Tests"))
|
||
|
||
# Path test results
|
||
self.path_results = QTextEdit()
|
||
self.path_results.setReadOnly(True)
|
||
layout.addWidget(self.path_results)
|
||
|
||
# Test buttons
|
||
btn_layout = QHBoxLayout()
|
||
|
||
test_sep_btn = QPushButton("Test Separators")
|
||
test_sep_btn.clicked.connect(self._test_path_separators)
|
||
btn_layout.addWidget(test_sep_btn)
|
||
|
||
test_resolve_btn = QPushButton("Test Resolution")
|
||
test_resolve_btn.clicked.connect(self._test_path_resolution)
|
||
btn_layout.addWidget(test_resolve_btn)
|
||
|
||
test_unicode_btn = QPushButton("Test Unicode")
|
||
test_unicode_btn.clicked.connect(self._test_unicode_paths)
|
||
btn_layout.addWidget(test_unicode_btn)
|
||
|
||
if self.IS_WINDOWS:
|
||
test_long_btn = QPushButton("Test Long Paths")
|
||
test_long_btn.clicked.connect(self._test_long_paths)
|
||
btn_layout.addWidget(test_long_btn)
|
||
|
||
layout.addLayout(btn_layout)
|
||
|
||
return widget
|
||
|
||
def _create_file_tab(self) -> QWidget:
|
||
"""Create the file tests tab."""
|
||
widget = QWidget()
|
||
layout = QVBoxLayout(widget)
|
||
|
||
layout.addWidget(QLabel("File Operations Tests"))
|
||
|
||
# File locking section
|
||
locking_group = QGroupBox("File Locking Tests")
|
||
locking_layout = QVBoxLayout(locking_group)
|
||
|
||
self.locking_results = QTextEdit()
|
||
self.locking_results.setReadOnly(True)
|
||
self.locking_results.setMaximumHeight(150)
|
||
locking_layout.addWidget(self.locking_results)
|
||
|
||
lock_btn_layout = QHBoxLayout()
|
||
|
||
test_fcntl_btn = QPushButton("Test fcntl")
|
||
test_fcntl_btn.clicked.connect(self._test_fcntl_locking)
|
||
test_fcntl_btn.setEnabled(self.IS_LINUX or self.IS_MAC)
|
||
lock_btn_layout.addWidget(test_fcntl_btn)
|
||
|
||
test_portalocker_btn = QPushButton("Test portalocker")
|
||
test_portalocker_btn.clicked.connect(self._test_portalocker_locking)
|
||
lock_btn_layout.addWidget(test_portalocker_btn)
|
||
|
||
test_threading_btn = QPushButton("Test Threading Lock")
|
||
test_threading_btn.clicked.connect(self._test_threading_lock)
|
||
lock_btn_layout.addWidget(test_threading_btn)
|
||
|
||
locking_layout.addLayout(lock_btn_layout)
|
||
layout.addWidget(locking_group)
|
||
|
||
# Permissions section
|
||
perm_group = QGroupBox("Permission Tests")
|
||
perm_layout = QVBoxLayout(perm_group)
|
||
|
||
self.perm_results = QTextEdit()
|
||
self.perm_results.setReadOnly(True)
|
||
self.perm_results.setMaximumHeight(100)
|
||
perm_layout.addWidget(self.perm_results)
|
||
|
||
test_perm_btn = QPushButton("Test File Permissions")
|
||
test_perm_btn.clicked.connect(self._test_file_permissions)
|
||
perm_layout.addWidget(test_perm_btn)
|
||
|
||
layout.addWidget(perm_group)
|
||
|
||
layout.addStretch()
|
||
return widget
|
||
|
||
def _create_system_tab(self) -> QWidget:
|
||
"""Create the system tests tab."""
|
||
widget = QWidget()
|
||
layout = QVBoxLayout(widget)
|
||
|
||
layout.addWidget(QLabel("System Information Tests"))
|
||
|
||
# System info display
|
||
self.system_info = QTextEdit()
|
||
self.system_info.setReadOnly(True)
|
||
layout.addWidget(self.system_info)
|
||
|
||
# Test buttons
|
||
btn_layout = QHBoxLayout()
|
||
|
||
cpu_btn = QPushButton("Get CPU Info")
|
||
cpu_btn.clicked.connect(self._test_cpu_info)
|
||
btn_layout.addWidget(cpu_btn)
|
||
|
||
mem_btn = QPushButton("Get Memory Info")
|
||
mem_btn.clicked.connect(self._test_memory_info)
|
||
btn_layout.addWidget(mem_btn)
|
||
|
||
env_btn = QPushButton("Test Environment")
|
||
env_btn.clicked.connect(self._test_environment)
|
||
btn_layout.addWidget(env_btn)
|
||
|
||
if self.IS_WINDOWS:
|
||
window_btn = QPushButton("Test Window Enum")
|
||
window_btn.clicked.connect(self._test_window_enumeration)
|
||
btn_layout.addWidget(window_btn)
|
||
|
||
layout.addLayout(btn_layout)
|
||
|
||
return widget
|
||
|
||
def _create_results_tab(self) -> QWidget:
|
||
"""Create the results tab."""
|
||
widget = QWidget()
|
||
layout = QVBoxLayout(widget)
|
||
|
||
layout.addWidget(QLabel("Test Results"))
|
||
|
||
# Results table
|
||
self.results_table = QTableWidget()
|
||
self.results_table.setColumnCount(5)
|
||
self.results_table.setHorizontalHeaderLabels([
|
||
"Test Name", "Category", "Status", "Details", "Platform"
|
||
])
|
||
self.results_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch)
|
||
layout.addWidget(self.results_table)
|
||
|
||
# Summary
|
||
self.results_summary = QLabel("No tests run yet")
|
||
layout.addWidget(self.results_summary)
|
||
|
||
# Export button
|
||
export_btn = QPushButton("Export Results")
|
||
export_btn.clicked.connect(self._export_results)
|
||
layout.addWidget(export_btn)
|
||
|
||
return widget
|
||
|
||
def _get_platform_details_html(self) -> str:
|
||
"""Get platform details as HTML."""
|
||
details = []
|
||
details.append("<h3>Platform Details</h3>")
|
||
details.append("<table border='1' cellpadding='5'>")
|
||
|
||
info = [
|
||
("System", platform.system()),
|
||
("Release", platform.release()),
|
||
("Version", platform.version()),
|
||
("Machine", platform.machine()),
|
||
("Processor", platform.processor() or 'Unknown'),
|
||
("Platform", platform.platform()),
|
||
("Python Version", platform.python_version()),
|
||
("Python Implementation", platform.python_implementation()),
|
||
("Python Compiler", platform.python_compiler()),
|
||
("Default Encoding", sys.getdefaultencoding()),
|
||
("Filesystem Encoding", sys.getfilesystemencoding()),
|
||
("Max Unicode", f"U+{sys.maxunicode:X}"),
|
||
("Executable", sys.executable),
|
||
("Prefix", sys.prefix),
|
||
]
|
||
|
||
for key, value in info:
|
||
details.append(f"<tr><td><b>{key}</b></td><td>{value}</td></tr>")
|
||
|
||
details.append("</table>")
|
||
return "\n".join(details)
|
||
|
||
def _get_feature_matrix_html(self) -> str:
|
||
"""Get feature support matrix as HTML."""
|
||
matrix = []
|
||
matrix.append("<table border='1' cellpadding='5'>")
|
||
matrix.append("<tr><th>Feature</th><th>Windows</th><th>Linux</th><th>macOS</th></tr>")
|
||
|
||
features = [
|
||
("Window Manager", "✅", "❌", "❌"),
|
||
("Native Hotkeys", "✅", "⚠️ Limited", "⚠️ Limited"),
|
||
("Global Hotkeys", "✅", "✅", "✅"),
|
||
("File Locking (fcntl)", "❌", "✅", "✅"),
|
||
("File Locking (portalocker)", "✅", "⚠️ Optional", "⚠️ Optional"),
|
||
("OCR (EasyOCR)", "✅", "✅", "✅"),
|
||
("OCR (Tesseract)", "✅", "✅", "✅"),
|
||
("Discord Webhooks", "✅", "✅", "✅"),
|
||
("MQTT", "✅", "✅", "✅"),
|
||
("WebSocket", "✅", "✅", "✅"),
|
||
("Long Paths (>260)", "✅*", "✅", "✅"),
|
||
]
|
||
|
||
for feature, win, lin, mac in features:
|
||
matrix.append(f"<tr><td>{feature}</td><td>{win}</td><td>{lin}</td><td>{mac}</td></tr>")
|
||
|
||
matrix.append("</table>")
|
||
matrix.append("<p>* Requires Windows 10 1607+ with long path support enabled</p>")
|
||
return "\n".join(matrix)
|
||
|
||
def _run_all_tests(self):
|
||
"""Run all compatibility tests."""
|
||
self._test_results.clear()
|
||
|
||
# Clear and setup results table
|
||
self.results_table.setRowCount(0)
|
||
|
||
for test in self.TEST_CASES:
|
||
# Skip platform-specific tests that don't apply
|
||
if test.windows_only and not self.IS_WINDOWS:
|
||
continue
|
||
if test.linux_only and not self.IS_LINUX:
|
||
continue
|
||
|
||
result = self._run_single_test(test)
|
||
self._test_results.append(result)
|
||
self._add_result_to_table(result)
|
||
|
||
self._update_results_summary()
|
||
|
||
def _run_single_test(self, test: PlatformTest) -> Dict:
|
||
"""Run a single test."""
|
||
result = {
|
||
"name": test.name,
|
||
"category": test.category,
|
||
"description": test.description,
|
||
"success": False,
|
||
"details": "",
|
||
"platform": "Windows" if test.windows_only else ("Linux" if test.linux_only else "All")
|
||
}
|
||
|
||
try:
|
||
if test.category == "path":
|
||
result.update(self._run_path_test(test))
|
||
elif test.category == "file":
|
||
result.update(self._run_file_test(test))
|
||
elif test.category == "process":
|
||
result.update(self._run_process_test(test))
|
||
elif test.category == "system":
|
||
result.update(self._run_system_test(test))
|
||
except Exception as e:
|
||
result["details"] = f"Error: {str(e)}"
|
||
|
||
return result
|
||
|
||
def _run_path_test(self, test: PlatformTest) -> Dict:
|
||
"""Run path-related tests."""
|
||
result = {"success": True, "details": ""}
|
||
|
||
if test.name == "Path Separator":
|
||
sep = os.sep
|
||
alt_sep = os.altsep
|
||
result["details"] = f"Primary: '{sep}', Alt: {repr(alt_sep)}"
|
||
|
||
elif test.name == "Path Resolution":
|
||
test_path = Path("~").expanduser()
|
||
result["details"] = f"Home resolved to: {test_path}"
|
||
|
||
elif test.name == "Unicode Paths":
|
||
# Test creating a path with Unicode
|
||
test_dir = Path(tempfile.gettempdir()) / "EU_Utility_测试"
|
||
result["details"] = f"Unicode path: {test_dir}"
|
||
|
||
elif test.name == "Long Paths":
|
||
if self.IS_WINDOWS:
|
||
# Windows long path test
|
||
result["details"] = "Long path support varies by Windows version"
|
||
|
||
return result
|
||
|
||
def _run_file_test(self, test: PlatformTest) -> Dict:
|
||
"""Run file-related tests."""
|
||
result = {"success": True, "details": ""}
|
||
|
||
if test.name == "File Locking - fcntl":
|
||
if self.IS_LINUX or self.IS_MAC:
|
||
try:
|
||
import fcntl
|
||
result["details"] = "fcntl module available"
|
||
except ImportError:
|
||
result["success"] = False
|
||
result["details"] = "fcntl not available"
|
||
else:
|
||
result["success"] = False
|
||
result["details"] = "Not applicable on Windows"
|
||
|
||
elif test.name == "File Locking - portalocker":
|
||
try:
|
||
import portalocker
|
||
result["details"] = "portalocker available"
|
||
except ImportError:
|
||
result["success"] = False
|
||
result["details"] = "portalocker not installed"
|
||
|
||
elif test.name == "File Permissions":
|
||
test_file = Path(tempfile.mktemp())
|
||
test_file.touch()
|
||
try:
|
||
mode = test_file.stat().st_mode
|
||
result["details"] = f"File mode: {oct(mode)}"
|
||
finally:
|
||
test_file.unlink()
|
||
|
||
return result
|
||
|
||
def _run_process_test(self, test: PlatformTest) -> Dict:
|
||
"""Run process-related tests."""
|
||
result = {"success": True, "details": ""}
|
||
|
||
if test.name == "Process List":
|
||
# Simple process list test
|
||
result["details"] = f"Current PID: {os.getpid()}"
|
||
|
||
elif test.name == "Window Enumeration":
|
||
if self.IS_WINDOWS:
|
||
try:
|
||
import ctypes
|
||
result["details"] = "ctypes available for window enum"
|
||
except ImportError:
|
||
result["success"] = False
|
||
result["details"] = "ctypes not available"
|
||
else:
|
||
result["success"] = False
|
||
result["details"] = "Not applicable on this platform"
|
||
|
||
return result
|
||
|
||
def _run_system_test(self, test: PlatformTest) -> Dict:
|
||
"""Run system-related tests."""
|
||
result = {"success": True, "details": ""}
|
||
|
||
if test.name == "CPU Info":
|
||
result["details"] = f"Processor: {platform.processor() or 'Unknown'}"
|
||
|
||
elif test.name == "Memory Info":
|
||
result["details"] = "Memory info requires psutil or platform-specific APIs"
|
||
|
||
elif test.name == "Environment Variables":
|
||
path_var = os.environ.get('PATH', '')[:100]
|
||
result["details"] = f"PATH starts with: {path_var}..."
|
||
|
||
return result
|
||
|
||
def _add_result_to_table(self, result: Dict):
|
||
"""Add a result to the results table."""
|
||
row = self.results_table.rowCount()
|
||
self.results_table.insertRow(row)
|
||
|
||
self.results_table.setItem(row, 0, QTableWidgetItem(result["name"]))
|
||
self.results_table.setItem(row, 1, QTableWidgetItem(result["category"]))
|
||
|
||
status = "✅ PASS" if result["success"] else "❌ FAIL"
|
||
status_item = QTableWidgetItem(status)
|
||
self.results_table.setItem(row, 2, status_item)
|
||
|
||
self.results_table.setItem(row, 3, QTableWidgetItem(result["details"]))
|
||
self.results_table.setItem(row, 4, QTableWidgetItem(result["platform"]))
|
||
|
||
def _update_results_summary(self):
|
||
"""Update results summary."""
|
||
passed = sum(1 for r in self._test_results if r["success"])
|
||
total = len(self._test_results)
|
||
|
||
self.results_summary.setText(f"Results: {passed}/{total} tests passed")
|
||
|
||
def _test_path_separators(self):
|
||
"""Test path separators."""
|
||
results = []
|
||
results.append(f"os.sep = '{os.sep}'")
|
||
results.append(f"os.altsep = {repr(os.altsep)}")
|
||
results.append(f"os.pathsep = '{os.pathsep}'")
|
||
results.append(f"os.linesep = {repr(os.linesep)}")
|
||
results.append("")
|
||
results.append("Pathlib tests:")
|
||
results.append(f"Path('/foo/bar').parts = {Path('/foo/bar').parts}")
|
||
|
||
self.path_results.setText("\n".join(results))
|
||
|
||
def _test_path_resolution(self):
|
||
"""Test path resolution."""
|
||
results = []
|
||
results.append(f"Path.home() = {Path.home()}")
|
||
results.append(f"Path.cwd() = {Path.cwd()}")
|
||
results.append(f"Path('~').expanduser() = {Path('~').expanduser()}")
|
||
|
||
# Test absolute/relative
|
||
test_path = Path("relative/path")
|
||
results.append(f"Path('relative/path').is_absolute() = {test_path.is_absolute()}")
|
||
results.append(f"Path('relative/path').resolve() = {test_path.resolve()}")
|
||
|
||
self.path_results.setText("\n".join(results))
|
||
|
||
def _test_unicode_paths(self):
|
||
"""Test Unicode path handling."""
|
||
results = []
|
||
|
||
test_names = [
|
||
"test_文件",
|
||
"test_🎮",
|
||
"test_ñáéíóú",
|
||
"test_مرحبا",
|
||
]
|
||
|
||
for name in test_names:
|
||
try:
|
||
test_path = Path(tempfile.gettempdir()) / name
|
||
test_path.mkdir(exist_ok=True)
|
||
exists = test_path.exists()
|
||
test_path.rmdir()
|
||
results.append(f"✅ {name}: OK")
|
||
except Exception as e:
|
||
results.append(f"❌ {name}: {str(e)}")
|
||
|
||
self.path_results.setText("\n".join(results))
|
||
|
||
def _test_long_paths(self):
|
||
"""Test long path handling."""
|
||
results = []
|
||
results.append("Long path support:")
|
||
results.append(f"Windows version: {platform.version()}")
|
||
results.append("")
|
||
results.append("Note: Windows 10 1607+ supports long paths")
|
||
results.append("when registry key is set and manifest declares support.")
|
||
|
||
self.path_results.setText("\n".join(results))
|
||
|
||
def _test_fcntl_locking(self):
|
||
"""Test fcntl file locking."""
|
||
results = []
|
||
|
||
try:
|
||
import fcntl
|
||
results.append("✅ fcntl module available")
|
||
|
||
# Create test file
|
||
test_file = tempfile.NamedTemporaryFile(delete=False)
|
||
test_path = test_file.name
|
||
test_file.close()
|
||
|
||
try:
|
||
with open(test_path, 'w') as f:
|
||
# Try to acquire lock
|
||
fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||
results.append("✅ Exclusive lock acquired")
|
||
|
||
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
|
||
results.append("✅ Lock released")
|
||
finally:
|
||
os.unlink(test_path)
|
||
|
||
except ImportError:
|
||
results.append("❌ fcntl not available (expected on Windows)")
|
||
except Exception as e:
|
||
results.append(f"❌ Error: {str(e)}")
|
||
|
||
self.locking_results.setText("\n".join(results))
|
||
|
||
def _test_portalocker_locking(self):
|
||
"""Test portalocker file locking."""
|
||
results = []
|
||
|
||
try:
|
||
import portalocker
|
||
results.append("✅ portalocker available")
|
||
results.append(f"Version: {getattr(portalocker, '__version__', 'unknown')}")
|
||
|
||
except ImportError:
|
||
results.append("❌ portalocker not installed")
|
||
results.append("Install with: pip install portalocker")
|
||
|
||
self.locking_results.setText("\n".join(results))
|
||
|
||
def _test_threading_lock(self):
|
||
"""Test threading lock as fallback."""
|
||
results = []
|
||
|
||
lock = threading.RLock()
|
||
|
||
results.append("Testing threading.RLock:")
|
||
results.append(f"✅ Lock created: {lock}")
|
||
|
||
with lock:
|
||
results.append("✅ Lock acquired in main thread")
|
||
|
||
results.append("✅ Lock released")
|
||
results.append("")
|
||
results.append("Threading locks work on all platforms")
|
||
results.append("but only within a single process.")
|
||
|
||
self.locking_results.setText("\n".join(results))
|
||
|
||
def _test_file_permissions(self):
|
||
"""Test file permissions."""
|
||
results = []
|
||
|
||
test_file = Path(tempfile.mktemp())
|
||
test_file.write_text("test")
|
||
|
||
try:
|
||
stat = test_file.stat()
|
||
results.append(f"File: {test_file}")
|
||
results.append(f"Mode: {oct(stat.st_mode)}")
|
||
results.append(f"UID: {stat.st_uid}")
|
||
results.append(f"GID: {stat.st_gid}")
|
||
results.append(f"Size: {stat.st_size} bytes")
|
||
|
||
if self.IS_WINDOWS:
|
||
results.append("")
|
||
results.append("Note: Windows uses ACLs, not Unix permissions")
|
||
finally:
|
||
test_file.unlink()
|
||
|
||
self.perm_results.setText("\n".join(results))
|
||
|
||
def _test_cpu_info(self):
|
||
"""Test CPU information retrieval."""
|
||
results = []
|
||
results.append(f"Platform: {platform.platform()}")
|
||
results.append(f"Processor: {platform.processor() or 'Unknown'}")
|
||
results.append(f"Machine: {platform.machine()}")
|
||
results.append(f"CPU Count: {os.cpu_count()}")
|
||
|
||
if self.IS_LINUX:
|
||
try:
|
||
with open('/proc/cpuinfo', 'r') as f:
|
||
for line in f:
|
||
if 'model name' in line:
|
||
results.append(f"CPU: {line.split(':')[1].strip()}")
|
||
break
|
||
except:
|
||
pass
|
||
|
||
self.system_info.setText("\n".join(results))
|
||
|
||
def _test_memory_info(self):
|
||
"""Test memory information retrieval."""
|
||
results = []
|
||
|
||
try:
|
||
import psutil
|
||
mem = psutil.virtual_memory()
|
||
results.append(f"Total: {mem.total / (1024**3):.2f} GB")
|
||
results.append(f"Available: {mem.available / (1024**3):.2f} GB")
|
||
results.append(f"Used: {mem.used / (1024**3):.2f} GB")
|
||
results.append(f"Percent: {mem.percent}%")
|
||
except ImportError:
|
||
results.append("psutil not installed")
|
||
results.append("Install with: pip install psutil")
|
||
|
||
if self.IS_LINUX:
|
||
try:
|
||
with open('/proc/meminfo', 'r') as f:
|
||
results.append("")
|
||
results.append("From /proc/meminfo:")
|
||
for _ in range(3):
|
||
results.append(f.read(100))
|
||
except:
|
||
pass
|
||
|
||
self.system_info.setText("\n".join(results))
|
||
|
||
def _test_environment(self):
|
||
"""Test environment variables."""
|
||
results = []
|
||
|
||
important_vars = [
|
||
'PATH', 'HOME', 'USERPROFILE', 'APPDATA', 'TEMP', 'TMP',
|
||
'PYTHONPATH', 'PYTHONHOME', 'QT_QPA_PLATFORM',
|
||
]
|
||
|
||
results.append("Environment Variables:")
|
||
results.append("")
|
||
|
||
for var in important_vars:
|
||
value = os.environ.get(var)
|
||
if value:
|
||
# Truncate long values
|
||
if len(value) > 80:
|
||
value = value[:77] + "..."
|
||
results.append(f"{var}={value}")
|
||
else:
|
||
results.append(f"{var}=(not set)")
|
||
|
||
self.system_info.setText("\n".join(results))
|
||
|
||
def _test_window_enumeration(self):
|
||
"""Test window enumeration (Windows)."""
|
||
results = []
|
||
|
||
if not self.IS_WINDOWS:
|
||
results.append("❌ Window enumeration only available on Windows")
|
||
self.system_info.setText("\n".join(results))
|
||
return
|
||
|
||
try:
|
||
import ctypes
|
||
from ctypes import wintypes
|
||
|
||
results.append("✅ ctypes available")
|
||
|
||
# Try to enumerate windows
|
||
user32 = ctypes.windll.user32
|
||
|
||
def callback(hwnd, extra):
|
||
if user32.IsWindowVisible(hwnd):
|
||
text = ctypes.create_unicode_buffer(256)
|
||
user32.GetWindowTextW(hwnd, text, 256)
|
||
if text.value:
|
||
extra.append(text.value)
|
||
return True
|
||
|
||
EnumWindowsProc = ctypes.WINFUNCTYPE(
|
||
wintypes.BOOL,
|
||
wintypes.HWND,
|
||
wintypes.LPARAM
|
||
)
|
||
|
||
windows = []
|
||
proc = EnumWindowsProc(callback)
|
||
user32.EnumWindows(proc, 0)
|
||
|
||
results.append(f"✅ Found {len(windows)} visible windows")
|
||
results.append("")
|
||
results.append("Sample windows:")
|
||
for title in windows[:10]:
|
||
results.append(f" - {title[:50]}")
|
||
|
||
except Exception as e:
|
||
results.append(f"❌ Error: {str(e)}")
|
||
|
||
self.system_info.setText("\n".join(results))
|
||
|
||
def _export_results(self):
|
||
"""Export test results."""
|
||
if not self._test_results:
|
||
self.notify_warning("No Results", "No test results to export")
|
||
return
|
||
|
||
filename = f"platform_compat_{sys.platform}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
||
|
||
export_data = {
|
||
"platform": {
|
||
"system": platform.system(),
|
||
"release": platform.release(),
|
||
"version": platform.version(),
|
||
"machine": platform.machine(),
|
||
"processor": platform.processor(),
|
||
"python": platform.python_version(),
|
||
},
|
||
"timestamp": datetime.now().isoformat(),
|
||
"results": self._test_results
|
||
}
|
||
|
||
with open(filename, 'w') as f:
|
||
json.dump(export_data, f, indent=2)
|
||
|
||
self.notify_success("Exported", f"Results saved to {filename}")
|
||
|
||
|
||
plugin_class = PlatformCompatibilityTester |