From 41efb585b3f6e87e81bbc31d215835462a53c40f Mon Sep 17 00:00:00 2001 From: cybrvybe Date: Wed, 11 Oct 2023 09:51:26 -0700 Subject: [PATCH] before WSL -> windows transfer --- .github/workflows/main.yml | 19 ++++++++------- .gitignore | 1 + Dockerfile.gui | 23 ++++++++++++++++++ cleanup.sh | 33 +++++++++++++++++++++++++ requirements.txt | 19 +++++++++++++++ run_tests.bat | 3 +++ tests/__init__.py | 0 tests/run_docker_tests.py | 28 ++++++++++++++++++++++ tests/test_process_launcher.py | 11 ++++++--- tests/test_window_manager.py | 41 +++++++++++++++++++++++++++---- utils/window_manager.py | 44 ++++++++++++++++++++++++---------- 11 files changed, 194 insertions(+), 28 deletions(-) create mode 100644 Dockerfile.gui create mode 100644 cleanup.sh create mode 100755 run_tests.bat create mode 100644 tests/__init__.py create mode 100644 tests/run_docker_tests.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1538884..ad0cc23 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,23 +1,26 @@ -name: Python CI +name: Workspace Automator CI/CD on: [push, pull_request] jobs: test: - - runs-on: ubuntu-latest + runs-on: windows-latest steps: - - name: Checkout code + - name: Checkout Code uses: actions/checkout@v2 + - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.x' - - name: Install dependencies + python-version: 3.9 # or your preferred version + + - name: Install Dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - - name: Run tests + pip install pytest + + - name: Run Tests run: | - # Commands to run your tests + pytest tests/ diff --git a/.gitignore b/.gitignore index b7bf860..df6544b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .venv/ profiles/ +__pycache__/ diff --git a/Dockerfile.gui b/Dockerfile.gui new file mode 100644 index 0000000..69a6772 --- /dev/null +++ b/Dockerfile.gui @@ -0,0 +1,23 @@ +# Use a Windows base image +FROM mcr.microsoft.com/windows/servercore:ltsc2019 + +# Install Python +ADD https://www.python.org/ftp/python/3.9.7/python-3.9.7-amd64.exe C:\\temp\\python-installer.exe +RUN C:\\temp\\python-installer.exe /quiet InstallAllUsers=1 PrependPath=1 && \ + del C:\\temp\\python-installer.exe + +# Upgrade pip +RUN python -m pip install --upgrade pip + +# Set working directory +WORKDIR /app + +# Copy requirements and install +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application +COPY . . + +# Command to run your tests (assuming you have a test runner script for Windows) +CMD ["run_tests.bat"] diff --git a/cleanup.sh b/cleanup.sh new file mode 100644 index 0000000..73d5150 --- /dev/null +++ b/cleanup.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# cleanup.sh + +# Directories to clean +DIRS_TO_CLEAN=("build" "codespace_utils.egg-info" "__pycache__" ".pytest_cache") + +echo "This script will clean up generated and temporary files." +read -p "Do you want to continue? (y/N): " confirmation + +if [[ "$confirmation" != "y" && "$confirmation" != "Y" ]]; then + echo "Cleanup canceled by the user." + exit 1 +fi + +# Recursively remove __pycache__ from all directories +find . -type d -name "__pycache__" -exec rm -rf {} + \ + && echo "Removed all __pycache__ directories." + +# Remove specified directories from the root of the project +for dir in "${DIRS_TO_CLEAN[@]}"; do + if [ -d "$dir" ]; then + rm -rf "$dir" + echo "Removed $dir." + else + echo "$dir does not exist, skipping." + fi +done + +# Optionally, you can also remove *.pyc files if they're outside of __pycache__ +# find . -type f -name "*.pyc" -delete + +echo "Cleanup complete! Remember to regularly commit important changes to version control." diff --git a/requirements.txt b/requirements.txt index e69de29..09027fe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,19 @@ +EasyProcess==1.1 +entrypoint2==1.1 +iniconfig==2.0.0 +jeepney==0.8.0 +MouseInfo==0.1.3 +mss==9.0.1 +packaging==23.2 +Pillow==10.0.1 +pluggy==1.3.0 +PyAutoGUI==0.9.54 +PyGetWindow==0.0.9 +PyMsgBox==1.0.9 +pyperclip==1.8.2 +PyRect==0.2.0 +pyscreenshot==3.1 +PyScreeze==0.1.29 +pytest==7.4.2 +python3-xlib==0.15 +pytweening==1.0.7 diff --git a/run_tests.bat b/run_tests.bat new file mode 100755 index 0000000..bec8bcf --- /dev/null +++ b/run_tests.bat @@ -0,0 +1,3 @@ +@echo off +REM Run pytest +python -m pytest diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/run_docker_tests.py b/tests/run_docker_tests.py new file mode 100644 index 0000000..33f3f24 --- /dev/null +++ b/tests/run_docker_tests.py @@ -0,0 +1,28 @@ +import os +import subprocess + +def build_docker_image(): + cmd = [ + "docker", "build", + "-t", "gui-tester", + "-f", "Dockerfile.gui", + "." + ] + subprocess.run(cmd, check=True) + +def run_docker_container(): + xauth_path = os.path.expanduser("~/.Xauthority") + cmd = ["docker", "run", "--rm", + "-v", f"{os.getcwd()}/tests/..:/app", + "-v", f"{xauth_path}:/root/.Xauthority", + "-e", "DISPLAY=unix:99", + "gui-tester", "/app/run_tests.sh"] + + subprocess.run(cmd, check=True) + +def main(): + build_docker_image() + run_docker_container() + +if __name__ == "__main__": + main() diff --git a/tests/test_process_launcher.py b/tests/test_process_launcher.py index 990ca67..89825f9 100644 --- a/tests/test_process_launcher.py +++ b/tests/test_process_launcher.py @@ -1,7 +1,12 @@ +import pytest +from unittest.mock import patch from utils.process_launcher import ProcessLauncher def test_process_launcher(): launcher = ProcessLauncher() - # You can mock the subprocess.Popen call or use a dummy safe application to test. - # For now, just a placeholder: - assert launcher + + # Mocking the subprocess call + with patch('utils.process_launcher.subprocess.Popen') as mocked_popen: + app = {"path": "dummy_path"} + launcher.launch(app) + mocked_popen.assert_called_once_with(["dummy_path"], shell=True) diff --git a/tests/test_window_manager.py b/tests/test_window_manager.py index ce4f72c..c2fb1db 100644 --- a/tests/test_window_manager.py +++ b/tests/test_window_manager.py @@ -1,7 +1,40 @@ from utils.window_manager import WindowManager +import subprocess +import time +import pytest +from unittest.mock import Mock -def test_window_manager(): +@pytest.fixture(autouse=True) +def mock_xlib(monkeypatch): + # Mock the entire Xlib library + mock_xlib = Mock() + monkeypatch.setattr("utils.window_manager.Xlib", mock_xlib) + +def test_window_manager_positioning(): manager = WindowManager() - # Testing GUI components like WindowManager can be tricky. Typically, you'd use mock objects or libraries. - # Again, just a placeholder for now: - assert manager + + # Launch a dummy X application + subprocess.Popen(["xeyes"]) + time.sleep(2) # give some time for the app to launch + + app = { + "name": "xeyes", + "position": { + "left": 0, + "top": 0, + "width": "50%", + "height": "100%" + } + } + + # Position the window using WindowManager + manager.position_window(app) + + # Here comes the tricky part: verifying the window's position and size. + # This would ideally involve querying the X server to get the window's properties + # and check if they match the expected values. Tools like `xwininfo` can be used + # to fetch this info, but parsing and verifying would be non-trivial. + + # As a placeholder, let's just check if xeyes is running (this is a very basic check) + result = subprocess.run(["pgrep", "xeyes"], stdout=subprocess.PIPE) + assert result.returncode == 0 # ensures xeyes process was found diff --git a/utils/window_manager.py b/utils/window_manager.py index 5c6164d..06725b0 100644 --- a/utils/window_manager.py +++ b/utils/window_manager.py @@ -1,4 +1,5 @@ import pyautogui +import pygetwindow as gw import time class WindowManager: @@ -6,30 +7,47 @@ def position_window(self, app): # Handle monitor selection and switch to the desired monitor self._switch_monitor(app["monitor"]) + # Wait for the application window to appear and get its title + time.sleep(2) + window = self._get_window_by_title(app["name"]) + # Handle window positioning if type(app["position"]) is dict: - self._position_window_by_coordinates(app["position"]) + self._position_window_by_coordinates(window, app["position"]) else: - self._position_window_by_keyword(app["position"]) + self._position_window_by_keyword(window, app["position"]) # Handle workspace shift if provided if "workspace" in app: self._switch_workspace(app["workspace"]) def _switch_monitor(self, monitor_number): - # Logic to switch to the desired monitor (this might require additional logic or libraries) - pass + # For simplicity, we'll just move the mouse to the desired monitor + # You can enhance this logic further + if monitor_number == 1: + pyautogui.moveTo(0, 0) + else: + pyautogui.moveTo(pyautogui.size().width / 2, 0) - def _position_window_by_coordinates(self, position): - # Logic to position window by given coordinates - # This can be based on the previous example using pyautogui - pass + def _position_window_by_coordinates(self, window, position): + # Adjust the window size and position + screen_width, screen_height = pyautogui.size() + width = position["width"] if type(position["width"]) is int else int(screen_width * float(position["width"].strip('%')) / 100) + height = position["height"] if type(position["height"]) is int else int(screen_height * float(position["height"].strip('%')) / 100) + window.resizeTo(width, height) + window.moveTo(position["left"], position["top"]) - def _position_window_by_keyword(self, position_keyword): + def _position_window_by_keyword(self, window, position_keyword): if position_keyword == "maximized": - # Logic to maximize the window (might require additional utilities or libraries) - pass + window.maximize() def _switch_workspace(self, workspace_number): - # Logic to switch to the desired workspace - pass + # Assuming Ctrl + Win + Left/Right switches workspaces + for _ in range(workspace_number - 1): + pyautogui.hotkey('ctrl', 'win', 'right') + + def _get_window_by_title(self, title): + for window in gw.getWindowsWithTitle(''): + if title.lower() in window.title.lower(): + return window + return None