flutter-dev 0.1.11__tar.gz → 0.1.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/CHANGELOG.md +21 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/PKG-INFO +8 -6
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/README.md +7 -5
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/fdev.py +6 -1
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/app.py +37 -31
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/package.py +74 -10
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/pyproject.toml +1 -1
- flutter_dev-0.1.13/tests/test_app_commands.py +161 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/tests/test_fdev_cli.py +20 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/tests/test_package_commands.py +125 -9
- flutter_dev-0.1.11/tests/test_app_commands.py +0 -66
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/.env.example +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/LICENSE +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/MANIFEST.in +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/common_utils.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/core/__init__.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/core/ai_config.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/core/constants.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/core/state.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/create_page.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/flutter_dev.egg-info/SOURCES.txt +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/gemini_api.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/git_diff_output_editor.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/install_legacy.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/__init__.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/ai.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/brew.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/build.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/datetime.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/device.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/doctor.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/git.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/git_account.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/localization.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/merge.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/mirror.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/project.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/managers/web_deploy.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/requirements.txt +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/setup.cfg +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/setup.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/switch_ai.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/tests/test_ai_config.py +0 -0
- {flutter_dev-0.1.11 → flutter_dev-0.1.13}/tests/test_localization.py +0 -0
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.1.13 - 2026-07-22
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `fdev -v` and `fdev --version` for showing the installed package version.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Changed `fdev upgrade` to check PyPI before invoking pip, skip installation when already current, suppress routine pip dependency output, and report verified before→after versions after a successful upgrade.
|
|
14
|
+
- Kept the terminal-restart reminder after an actual successful version change.
|
|
15
|
+
|
|
16
|
+
## 0.1.12 - 2026-07-22
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Updated the PyPI workflow to use Node.js 24-native `actions/upload-artifact@v7`.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Fixed `fdev clear-data` to verify that the current project app is installed before clearing it, and to fall back to a confirmed foreground-app clear when the project app is missing or the direct clear fails.
|
|
25
|
+
|
|
5
26
|
## 0.1.11 - 2026-07-22
|
|
6
27
|
|
|
7
28
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flutter-dev
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: A cross-platform CLI toolkit for Flutter development workflows.
|
|
5
5
|
Author: Royal Court BD
|
|
6
6
|
License-Expression: MIT
|
|
@@ -42,7 +42,7 @@ The package is published on PyPI as `flutter-dev` and installs console commands
|
|
|
42
42
|
python -m pip install flutter-dev
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Current package metadata declares version `0.1.
|
|
45
|
+
Current package metadata declares version `0.1.13`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
|
|
46
46
|
|
|
47
47
|
## Key Features
|
|
48
48
|
|
|
@@ -201,7 +201,7 @@ Android APK/AAB build commands rename output files using the Android app label a
|
|
|
201
201
|
| --- | --- |
|
|
202
202
|
| `fdev install` | Installs a built APK from `build/app/outputs/flutter-apk`, prefers `arm64-v8a`, retries after uninstall on failure, then launches the app. |
|
|
203
203
|
| `fdev uninstall` | In a Flutter project root, uninstalls by package/bundle id. Outside a project, detects the foreground app and asks for confirmation. |
|
|
204
|
-
| `fdev clear-data` | In a Flutter project root, clears the project's Android package data without confirmation
|
|
204
|
+
| `fdev clear-data` | In a Flutter project root, clears the installed project's Android package data without confirmation; if it is missing or the clear fails, falls back to the foreground app with a default-Yes safety prompt. Outside a project, always detects and confirms the foreground app. iOS prints manual options. |
|
|
205
205
|
| `fdev install-scrcpy` | Installs `scrcpy` using the best supported package manager for the current platform. |
|
|
206
206
|
| `fdev mirror` | Launches `scrcpy` for a selected Android device. |
|
|
207
207
|
| `fdev mirror --wireless` | Guides wireless ADB setup using a USB-connected Android device. |
|
|
@@ -241,9 +241,10 @@ Switching GitHub accounts runs `gh auth switch` and `gh auth setup-git`, but it
|
|
|
241
241
|
| --- | --- |
|
|
242
242
|
| `fdev` | Shows command help. |
|
|
243
243
|
| `fdev help` | Shows command help. |
|
|
244
|
+
| `fdev -v` / `fdev --version` | Shows the version installed in the active fdev Python environment. |
|
|
244
245
|
| `fdev doctor` | Checks platform, Python, Flutter/Dart/Git, Python packages, Android/iOS tools, optional CLIs, `.env`, Flutter project detection, and global `fdev` availability. |
|
|
245
246
|
| `fdev init` | Creates or repairs the persistent AI `.env` with safe provider defaults and empty API keys. |
|
|
246
|
-
| `fdev upgrade` |
|
|
247
|
+
| `fdev upgrade` | Checks the latest PyPI version first. If already current, skips pip and prints one up-to-date notice; otherwise upgrades quietly and reports the previous and installed versions. |
|
|
247
248
|
| `fdev delete` | Detects the OS, asks for confirmation, then uninstalls the active Python environment's `flutter-dev` package. Use `fdev delete --yes` to skip confirmation. |
|
|
248
249
|
| `fdev ai` | Shows the current AI provider and opens an interactive provider switch prompt. |
|
|
249
250
|
| `fdev ai <provider>` | Switches `DEFAULT_AI_SERVICE` to `groq`, `mistral`, `sambanova`, or `openrouter`. |
|
|
@@ -413,7 +414,7 @@ The installed Python layout is top-level modules plus the `core` and `managers`
|
|
|
413
414
|
- `fdev tag` updates `pubspec.yaml`, creates a commit, creates a Git tag, and pushes both.
|
|
414
415
|
- `fdev discard` removes tracked changes and untracked files after confirmation.
|
|
415
416
|
- `fdev cleanup` applies `dart fix`, formats the project, and runs a major-version dependency upgrade.
|
|
416
|
-
- `fdev clear-data`
|
|
417
|
+
- `fdev clear-data` clears the installed current project's Android package without confirmation when run from a Flutter project root. If that package is missing or cannot be cleared, and whenever the command runs outside a project, it targets the foreground app after a default-Yes `[Y/n]` confirmation.
|
|
417
418
|
- `fdev uninstall` may fall back to foreground app detection outside a Flutter project root.
|
|
418
419
|
- `fdev upgrade` and `fdev delete` manage the local `flutter-dev` Python package, not the Flutter app on a device.
|
|
419
420
|
- `fdev upgrade` reports the installed version and location before upgrading, then shows the before→after version when the active process can verify it immediately.
|
|
@@ -451,11 +452,12 @@ where fdev
|
|
|
451
452
|
Use the built-in package management commands from the Python environment where `fdev` is installed:
|
|
452
453
|
|
|
453
454
|
```bash
|
|
455
|
+
fdev -v
|
|
454
456
|
fdev upgrade
|
|
455
457
|
fdev delete
|
|
456
458
|
```
|
|
457
459
|
|
|
458
|
-
|
|
460
|
+
The upgrade and delete commands print the detected OS and exact Python executable. Upgrade checks PyPI first and avoids running pip when the installed version is current. When pip is needed, both commands use the active executable with `-m pip`, not a hardcoded `python`, `python3`, `pip`, or `pip3`. On Windows, `fdev delete` and a required `fdev upgrade` schedule the same `sys.executable -m pip ...` operation in a delayed command process so the currently running console launcher can exit first.
|
|
459
461
|
|
|
460
462
|
### Flutter Project Not Detected
|
|
461
463
|
|
|
@@ -8,7 +8,7 @@ The package is published on PyPI as `flutter-dev` and installs console commands
|
|
|
8
8
|
python -m pip install flutter-dev
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Current package metadata declares version `0.1.
|
|
11
|
+
Current package metadata declares version `0.1.13`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
|
|
12
12
|
|
|
13
13
|
## Key Features
|
|
14
14
|
|
|
@@ -167,7 +167,7 @@ Android APK/AAB build commands rename output files using the Android app label a
|
|
|
167
167
|
| --- | --- |
|
|
168
168
|
| `fdev install` | Installs a built APK from `build/app/outputs/flutter-apk`, prefers `arm64-v8a`, retries after uninstall on failure, then launches the app. |
|
|
169
169
|
| `fdev uninstall` | In a Flutter project root, uninstalls by package/bundle id. Outside a project, detects the foreground app and asks for confirmation. |
|
|
170
|
-
| `fdev clear-data` | In a Flutter project root, clears the project's Android package data without confirmation
|
|
170
|
+
| `fdev clear-data` | In a Flutter project root, clears the installed project's Android package data without confirmation; if it is missing or the clear fails, falls back to the foreground app with a default-Yes safety prompt. Outside a project, always detects and confirms the foreground app. iOS prints manual options. |
|
|
171
171
|
| `fdev install-scrcpy` | Installs `scrcpy` using the best supported package manager for the current platform. |
|
|
172
172
|
| `fdev mirror` | Launches `scrcpy` for a selected Android device. |
|
|
173
173
|
| `fdev mirror --wireless` | Guides wireless ADB setup using a USB-connected Android device. |
|
|
@@ -207,9 +207,10 @@ Switching GitHub accounts runs `gh auth switch` and `gh auth setup-git`, but it
|
|
|
207
207
|
| --- | --- |
|
|
208
208
|
| `fdev` | Shows command help. |
|
|
209
209
|
| `fdev help` | Shows command help. |
|
|
210
|
+
| `fdev -v` / `fdev --version` | Shows the version installed in the active fdev Python environment. |
|
|
210
211
|
| `fdev doctor` | Checks platform, Python, Flutter/Dart/Git, Python packages, Android/iOS tools, optional CLIs, `.env`, Flutter project detection, and global `fdev` availability. |
|
|
211
212
|
| `fdev init` | Creates or repairs the persistent AI `.env` with safe provider defaults and empty API keys. |
|
|
212
|
-
| `fdev upgrade` |
|
|
213
|
+
| `fdev upgrade` | Checks the latest PyPI version first. If already current, skips pip and prints one up-to-date notice; otherwise upgrades quietly and reports the previous and installed versions. |
|
|
213
214
|
| `fdev delete` | Detects the OS, asks for confirmation, then uninstalls the active Python environment's `flutter-dev` package. Use `fdev delete --yes` to skip confirmation. |
|
|
214
215
|
| `fdev ai` | Shows the current AI provider and opens an interactive provider switch prompt. |
|
|
215
216
|
| `fdev ai <provider>` | Switches `DEFAULT_AI_SERVICE` to `groq`, `mistral`, `sambanova`, or `openrouter`. |
|
|
@@ -379,7 +380,7 @@ The installed Python layout is top-level modules plus the `core` and `managers`
|
|
|
379
380
|
- `fdev tag` updates `pubspec.yaml`, creates a commit, creates a Git tag, and pushes both.
|
|
380
381
|
- `fdev discard` removes tracked changes and untracked files after confirmation.
|
|
381
382
|
- `fdev cleanup` applies `dart fix`, formats the project, and runs a major-version dependency upgrade.
|
|
382
|
-
- `fdev clear-data`
|
|
383
|
+
- `fdev clear-data` clears the installed current project's Android package without confirmation when run from a Flutter project root. If that package is missing or cannot be cleared, and whenever the command runs outside a project, it targets the foreground app after a default-Yes `[Y/n]` confirmation.
|
|
383
384
|
- `fdev uninstall` may fall back to foreground app detection outside a Flutter project root.
|
|
384
385
|
- `fdev upgrade` and `fdev delete` manage the local `flutter-dev` Python package, not the Flutter app on a device.
|
|
385
386
|
- `fdev upgrade` reports the installed version and location before upgrading, then shows the before→after version when the active process can verify it immediately.
|
|
@@ -417,11 +418,12 @@ where fdev
|
|
|
417
418
|
Use the built-in package management commands from the Python environment where `fdev` is installed:
|
|
418
419
|
|
|
419
420
|
```bash
|
|
421
|
+
fdev -v
|
|
420
422
|
fdev upgrade
|
|
421
423
|
fdev delete
|
|
422
424
|
```
|
|
423
425
|
|
|
424
|
-
|
|
426
|
+
The upgrade and delete commands print the detected OS and exact Python executable. Upgrade checks PyPI first and avoids running pip when the installed version is current. When pip is needed, both commands use the active executable with `-m pip`, not a hardcoded `python`, `python3`, `pip`, or `pip3`. On Windows, `fdev delete` and a required `fdev upgrade` schedule the same `sys.executable -m pip ...` operation in a delayed command process so the currently running console launcher can exit first.
|
|
425
427
|
|
|
426
428
|
### Flutter Project Not Detected
|
|
427
429
|
|
|
@@ -73,6 +73,7 @@ from managers.git_account import (
|
|
|
73
73
|
)
|
|
74
74
|
from managers.package import (
|
|
75
75
|
delete_fdev_command,
|
|
76
|
+
show_fdev_version,
|
|
76
77
|
upgrade_fdev_command,
|
|
77
78
|
)
|
|
78
79
|
|
|
@@ -102,7 +103,7 @@ def show_usage(exit_code=1):
|
|
|
102
103
|
print(f"\n{BLUE}Device Commands:{NC}")
|
|
103
104
|
print(" install Install built APK on connected device")
|
|
104
105
|
print(" uninstall Uninstall app from connected device")
|
|
105
|
-
print(" clear-data Clear project app data
|
|
106
|
+
print(" clear-data Clear project app data with confirmed foreground fallback")
|
|
106
107
|
print(" install-scrcpy Install scrcpy for Android screen mirroring")
|
|
107
108
|
print(" mirror Launch scrcpy screen mirror (auto-detect device)")
|
|
108
109
|
print(" --wireless Setup wireless ADB connection")
|
|
@@ -124,6 +125,7 @@ def show_usage(exit_code=1):
|
|
|
124
125
|
print(" brew Interactive Homebrew package manager")
|
|
125
126
|
print(" upgrade Upgrade fdev from PyPI")
|
|
126
127
|
print(" delete Uninstall fdev from this computer")
|
|
128
|
+
print(" -v, --version Show the installed fdev version")
|
|
127
129
|
print(" help Show this help")
|
|
128
130
|
print(" merge Merge files from paths.txt into single output file")
|
|
129
131
|
print(" ai Show current AI service or switch to another")
|
|
@@ -167,6 +169,9 @@ def main():
|
|
|
167
169
|
|
|
168
170
|
command = sys.argv[1].lower()
|
|
169
171
|
|
|
172
|
+
if command in ("-v", "--version"):
|
|
173
|
+
return show_fdev_version()
|
|
174
|
+
|
|
170
175
|
if command in ("apk", "apk-split", "aab", "ipa", "release-run", "install"):
|
|
171
176
|
os.makedirs("build/app/outputs/flutter-apk", exist_ok=True)
|
|
172
177
|
os.makedirs("build/app/outputs/bundle/release", exist_ok=True)
|
|
@@ -188,14 +188,15 @@ def clear_app_data():
|
|
|
188
188
|
"""
|
|
189
189
|
Clear app data from a connected device.
|
|
190
190
|
|
|
191
|
-
In a Flutter project root, clear the project's Android package
|
|
192
|
-
confirmation
|
|
193
|
-
|
|
191
|
+
In a Flutter project root, clear the project's installed Android package
|
|
192
|
+
without confirmation. If it is not installed or cannot be cleared, fall
|
|
193
|
+
back to the foreground app with confirmation. Outside a project root,
|
|
194
|
+
always detect and confirm before clearing the foreground app.
|
|
194
195
|
"""
|
|
195
196
|
print(f"{YELLOW}Clearing app data...{NC}\n")
|
|
196
197
|
|
|
197
|
-
# Match uninstall behavior in a Flutter project: target the
|
|
198
|
-
# package
|
|
198
|
+
# Match uninstall behavior in a Flutter project: target the installed
|
|
199
|
+
# project package first, then fall back to the foreground app if needed.
|
|
199
200
|
if is_flutter_project_root() and get_all_connected_devices():
|
|
200
201
|
print(f"{GREEN}✓ Flutter project detected{NC}\n")
|
|
201
202
|
print(f"{BLUE}Android device detected{NC}")
|
|
@@ -210,21 +211,40 @@ def clear_app_data():
|
|
|
210
211
|
|
|
211
212
|
print(f"{BLUE}Package name: {package_name}{NC}\n")
|
|
212
213
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
if is_app_installed(package_name):
|
|
215
|
+
print(f"{GREEN}✓ App is installed on device{NC}\n")
|
|
216
|
+
success = _clear_android_package_data(package_name)
|
|
217
|
+
if success:
|
|
218
|
+
return True
|
|
217
219
|
|
|
218
|
-
|
|
219
|
-
print(f"\n{GREEN}✓ App data cleared successfully!{NC}")
|
|
220
|
-
print(f"{BLUE}You can now relaunch the app from the device{NC}")
|
|
220
|
+
print(f"\n{YELLOW}⚠️ Could not clear data for {package_name}{NC}")
|
|
221
221
|
else:
|
|
222
|
-
print(f"
|
|
223
|
-
print(f"{YELLOW}Make sure the app package name is correct{NC}")
|
|
222
|
+
print(f"{YELLOW}⚠️ Project app is not installed on device{NC}")
|
|
224
223
|
|
|
225
|
-
|
|
224
|
+
print(f"{YELLOW} Detecting foreground app instead...{NC}\n")
|
|
226
225
|
|
|
227
|
-
|
|
226
|
+
return _clear_foreground_app_data()
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _clear_android_package_data(package_name):
|
|
230
|
+
"""Clear one Android package and report the command result."""
|
|
231
|
+
success = run_flutter_command(
|
|
232
|
+
build_adb_cmd(["shell", "pm", "clear", package_name]),
|
|
233
|
+
"Clearing app data... ",
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if success:
|
|
237
|
+
print(f"\n{GREEN}✓ App data cleared successfully!{NC}")
|
|
238
|
+
print(f"{BLUE}You can now relaunch the app from the device{NC}")
|
|
239
|
+
else:
|
|
240
|
+
print(f"\n{RED}✗ Failed to clear app data!{NC}")
|
|
241
|
+
print(f"{YELLOW}Make sure the app package name is correct{NC}")
|
|
242
|
+
|
|
243
|
+
return success
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _clear_foreground_app_data():
|
|
247
|
+
"""Detect the foreground app and clear it only after confirmation."""
|
|
228
248
|
platform_type, package_name = get_current_foreground_app()
|
|
229
249
|
|
|
230
250
|
if not platform_type:
|
|
@@ -247,22 +267,8 @@ def clear_app_data():
|
|
|
247
267
|
print(f"{YELLOW}Operation cancelled{NC}")
|
|
248
268
|
return False
|
|
249
269
|
|
|
250
|
-
# Clear data based on platform
|
|
251
270
|
if platform_type == "android":
|
|
252
|
-
|
|
253
|
-
success = run_flutter_command(
|
|
254
|
-
build_adb_cmd(["shell", "pm", "clear", package_name]),
|
|
255
|
-
f"Clearing app data... "
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
if success:
|
|
259
|
-
print(f"\n{GREEN}✓ App data cleared successfully!{NC}")
|
|
260
|
-
print(f"{BLUE}You can now relaunch the app from the device{NC}")
|
|
261
|
-
else:
|
|
262
|
-
print(f"\n{RED}✗ Failed to clear app data!{NC}")
|
|
263
|
-
print(f"{YELLOW}Make sure the app package name is correct{NC}")
|
|
264
|
-
|
|
265
|
-
return success
|
|
271
|
+
return _clear_android_package_data(package_name)
|
|
266
272
|
|
|
267
273
|
elif platform_type == "ios":
|
|
268
274
|
# iOS doesn't support direct app data clearing via command line
|
|
@@ -9,9 +9,12 @@ import platform
|
|
|
9
9
|
import subprocess
|
|
10
10
|
import sys
|
|
11
11
|
|
|
12
|
+
import requests
|
|
13
|
+
|
|
12
14
|
from common_utils import BLUE, GREEN, RED, YELLOW, NC
|
|
13
15
|
|
|
14
16
|
PACKAGE_NAME = "flutter-dev"
|
|
17
|
+
PYPI_JSON_URL = f"https://pypi.org/pypi/{PACKAGE_NAME}/json"
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
def get_os_info():
|
|
@@ -59,6 +62,32 @@ def _print_installed_package_info(version, package_location):
|
|
|
59
62
|
print(f"{BLUE}Package location: {GREEN}{package_location}{NC}")
|
|
60
63
|
|
|
61
64
|
|
|
65
|
+
def show_fdev_version():
|
|
66
|
+
"""Print the version installed in the Python environment running fdev."""
|
|
67
|
+
version, _ = _get_installed_package_info()
|
|
68
|
+
if version == "not installed":
|
|
69
|
+
print(f"{RED}Could not determine the installed fdev version.{NC}")
|
|
70
|
+
return 1
|
|
71
|
+
|
|
72
|
+
print(f"fdev {version}")
|
|
73
|
+
return 0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _get_latest_pypi_version():
|
|
77
|
+
"""Return the latest published PyPI version, or None when unavailable."""
|
|
78
|
+
try:
|
|
79
|
+
response = requests.get(
|
|
80
|
+
PYPI_JSON_URL,
|
|
81
|
+
headers={"Accept": "application/json"},
|
|
82
|
+
timeout=10,
|
|
83
|
+
)
|
|
84
|
+
response.raise_for_status()
|
|
85
|
+
version = response.json()["info"]["version"]
|
|
86
|
+
return version.strip() if isinstance(version, str) and version.strip() else None
|
|
87
|
+
except (requests.RequestException, KeyError, TypeError, ValueError):
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
62
91
|
def _build_pip_command(pip_args):
|
|
63
92
|
return [sys.executable, "-m", "pip", *pip_args]
|
|
64
93
|
|
|
@@ -68,6 +97,23 @@ def _run_pip_direct(pip_args):
|
|
|
68
97
|
return result.returncode
|
|
69
98
|
|
|
70
99
|
|
|
100
|
+
def _run_pip_quiet(pip_args):
|
|
101
|
+
"""Run pip without streaming routine dependency output to the terminal."""
|
|
102
|
+
result = subprocess.run(
|
|
103
|
+
_build_pip_command(pip_args),
|
|
104
|
+
capture_output=True,
|
|
105
|
+
text=True,
|
|
106
|
+
encoding="utf-8",
|
|
107
|
+
errors="replace",
|
|
108
|
+
)
|
|
109
|
+
if result.returncode != 0:
|
|
110
|
+
if result.stdout:
|
|
111
|
+
print(f"{GREEN}Output:\n{result.stdout.strip()}{NC}")
|
|
112
|
+
if result.stderr:
|
|
113
|
+
print(f"{RED}Error Output:\n{result.stderr.strip()}{NC}")
|
|
114
|
+
return result.returncode
|
|
115
|
+
|
|
116
|
+
|
|
71
117
|
def _build_windows_detached_command(pip_args):
|
|
72
118
|
pip_command = subprocess.list2cmdline(_build_pip_command(pip_args))
|
|
73
119
|
delayed_command = f"timeout /t 2 /nobreak > nul & {pip_command}"
|
|
@@ -158,12 +204,24 @@ def upgrade_fdev_command(argv=None):
|
|
|
158
204
|
|
|
159
205
|
pip_args = ["install", "--upgrade", PACKAGE_NAME]
|
|
160
206
|
print(f"{BLUE}Upgrade source: {GREEN}PyPI{NC}")
|
|
161
|
-
|
|
207
|
+
latest_version = _get_latest_pypi_version()
|
|
208
|
+
|
|
209
|
+
if latest_version == current_version:
|
|
210
|
+
print(f"{GREEN}✓ Already up to date: {current_version}{NC}")
|
|
211
|
+
return 0
|
|
212
|
+
|
|
213
|
+
if latest_version:
|
|
214
|
+
print(
|
|
215
|
+
f"{YELLOW}Upgrading {PACKAGE_NAME}: "
|
|
216
|
+
f"{current_version} → {latest_version}...{NC}"
|
|
217
|
+
)
|
|
218
|
+
else:
|
|
219
|
+
print(f"{YELLOW}Could not verify the latest PyPI version; checking with pip...{NC}")
|
|
162
220
|
|
|
163
221
|
if os_key == "windows":
|
|
164
222
|
return _run_windows_detached(pip_args, "Upgrade")
|
|
165
223
|
|
|
166
|
-
result =
|
|
224
|
+
result = _run_pip_quiet(pip_args)
|
|
167
225
|
if result == 0:
|
|
168
226
|
installed_version, installed_location = _get_installed_package_info()
|
|
169
227
|
if current_version != installed_version:
|
|
@@ -171,14 +229,20 @@ def upgrade_fdev_command(argv=None):
|
|
|
171
229
|
f"{GREEN}✓ {PACKAGE_NAME} upgraded successfully: "
|
|
172
230
|
f"{current_version} → {installed_version}.{NC}"
|
|
173
231
|
)
|
|
174
|
-
|
|
232
|
+
if installed_location != package_location:
|
|
233
|
+
print(f"{BLUE}Installed at: {GREEN}{installed_location}{NC}")
|
|
234
|
+
print(f"{BLUE}Restart your terminal if the fdev command still uses the old version.{NC}")
|
|
235
|
+
return 0
|
|
236
|
+
|
|
237
|
+
if latest_version and latest_version != installed_version:
|
|
175
238
|
print(
|
|
176
|
-
f"{
|
|
177
|
-
f"Installed
|
|
239
|
+
f"{RED}✗ Upgrade did not change the installed version. "
|
|
240
|
+
f"Installed: {installed_version}; PyPI: {latest_version}.{NC}"
|
|
178
241
|
)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
print(f"{
|
|
182
|
-
|
|
183
|
-
|
|
242
|
+
return 1
|
|
243
|
+
|
|
244
|
+
print(f"{GREEN}✓ Already up to date: {installed_version}{NC}")
|
|
245
|
+
return 0
|
|
246
|
+
|
|
247
|
+
print(f"{RED}✗ Failed to upgrade {PACKAGE_NAME}.{NC}")
|
|
184
248
|
return result
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
|
|
4
|
+
from managers import app
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ClearAppDataTests(unittest.TestCase):
|
|
8
|
+
def test_project_root_clears_project_package_without_confirmation(self):
|
|
9
|
+
with (
|
|
10
|
+
patch.object(app, "is_flutter_project_root", return_value=True),
|
|
11
|
+
patch.object(app, "get_all_connected_devices", return_value=["device-1"]),
|
|
12
|
+
patch.object(app, "ensure_device_connected", return_value=True),
|
|
13
|
+
patch.object(app, "get_package_name", return_value="com.example.app"),
|
|
14
|
+
patch.object(app, "is_app_installed", return_value=True),
|
|
15
|
+
patch.object(
|
|
16
|
+
app,
|
|
17
|
+
"build_adb_cmd",
|
|
18
|
+
return_value=["adb", "shell", "pm", "clear", "com.example.app"],
|
|
19
|
+
),
|
|
20
|
+
patch.object(app, "run_flutter_command", return_value=True) as run_command,
|
|
21
|
+
patch.object(app, "get_current_foreground_app") as foreground_app,
|
|
22
|
+
patch("builtins.input") as user_input,
|
|
23
|
+
):
|
|
24
|
+
result = app.clear_app_data()
|
|
25
|
+
|
|
26
|
+
self.assertTrue(result)
|
|
27
|
+
foreground_app.assert_not_called()
|
|
28
|
+
user_input.assert_not_called()
|
|
29
|
+
run_command.assert_called_once_with(
|
|
30
|
+
["adb", "shell", "pm", "clear", "com.example.app"],
|
|
31
|
+
"Clearing app data... ",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def test_missing_project_app_falls_back_to_confirmed_foreground_app(self):
|
|
35
|
+
with (
|
|
36
|
+
patch.object(app, "is_flutter_project_root", return_value=True),
|
|
37
|
+
patch.object(app, "get_all_connected_devices", return_value=["device-1"]),
|
|
38
|
+
patch.object(app, "ensure_device_connected", return_value=True),
|
|
39
|
+
patch.object(app, "get_package_name", return_value="com.example.app"),
|
|
40
|
+
patch.object(app, "is_app_installed", return_value=False),
|
|
41
|
+
patch.object(
|
|
42
|
+
app,
|
|
43
|
+
"get_current_foreground_app",
|
|
44
|
+
return_value=("android", "com.example.foreground"),
|
|
45
|
+
) as foreground_app,
|
|
46
|
+
patch.object(
|
|
47
|
+
app,
|
|
48
|
+
"build_adb_cmd",
|
|
49
|
+
return_value=[
|
|
50
|
+
"adb",
|
|
51
|
+
"shell",
|
|
52
|
+
"pm",
|
|
53
|
+
"clear",
|
|
54
|
+
"com.example.foreground",
|
|
55
|
+
],
|
|
56
|
+
),
|
|
57
|
+
patch.object(app, "run_flutter_command", return_value=True) as run_command,
|
|
58
|
+
patch("builtins.input", return_value="") as user_input,
|
|
59
|
+
):
|
|
60
|
+
result = app.clear_app_data()
|
|
61
|
+
|
|
62
|
+
self.assertTrue(result)
|
|
63
|
+
foreground_app.assert_called_once_with()
|
|
64
|
+
user_input.assert_called_once()
|
|
65
|
+
run_command.assert_called_once_with(
|
|
66
|
+
["adb", "shell", "pm", "clear", "com.example.foreground"],
|
|
67
|
+
"Clearing app data... ",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def test_failed_project_clear_falls_back_to_confirmed_foreground_app(self):
|
|
71
|
+
def adb_command(parts):
|
|
72
|
+
return ["adb", *parts]
|
|
73
|
+
|
|
74
|
+
with (
|
|
75
|
+
patch.object(app, "is_flutter_project_root", return_value=True),
|
|
76
|
+
patch.object(app, "get_all_connected_devices", return_value=["device-1"]),
|
|
77
|
+
patch.object(app, "ensure_device_connected", return_value=True),
|
|
78
|
+
patch.object(app, "get_package_name", return_value="com.example.app"),
|
|
79
|
+
patch.object(app, "is_app_installed", return_value=True),
|
|
80
|
+
patch.object(
|
|
81
|
+
app,
|
|
82
|
+
"get_current_foreground_app",
|
|
83
|
+
return_value=("android", "com.example.foreground"),
|
|
84
|
+
),
|
|
85
|
+
patch.object(app, "build_adb_cmd", side_effect=adb_command),
|
|
86
|
+
patch.object(
|
|
87
|
+
app,
|
|
88
|
+
"run_flutter_command",
|
|
89
|
+
side_effect=[False, True],
|
|
90
|
+
) as run_command,
|
|
91
|
+
patch("builtins.input", return_value="") as user_input,
|
|
92
|
+
):
|
|
93
|
+
result = app.clear_app_data()
|
|
94
|
+
|
|
95
|
+
self.assertTrue(result)
|
|
96
|
+
user_input.assert_called_once()
|
|
97
|
+
self.assertEqual(run_command.call_count, 2)
|
|
98
|
+
self.assertEqual(
|
|
99
|
+
run_command.call_args_list[0].args[0],
|
|
100
|
+
["adb", "shell", "pm", "clear", "com.example.app"],
|
|
101
|
+
)
|
|
102
|
+
self.assertEqual(
|
|
103
|
+
run_command.call_args_list[1].args[0],
|
|
104
|
+
["adb", "shell", "pm", "clear", "com.example.foreground"],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def test_missing_project_app_foreground_fallback_can_be_cancelled(self):
|
|
108
|
+
with (
|
|
109
|
+
patch.object(app, "is_flutter_project_root", return_value=True),
|
|
110
|
+
patch.object(app, "get_all_connected_devices", return_value=["device-1"]),
|
|
111
|
+
patch.object(app, "ensure_device_connected", return_value=True),
|
|
112
|
+
patch.object(app, "get_package_name", return_value="com.example.app"),
|
|
113
|
+
patch.object(app, "is_app_installed", return_value=False),
|
|
114
|
+
patch.object(
|
|
115
|
+
app,
|
|
116
|
+
"get_current_foreground_app",
|
|
117
|
+
return_value=("android", "com.example.foreground"),
|
|
118
|
+
),
|
|
119
|
+
patch.object(app, "run_flutter_command") as run_command,
|
|
120
|
+
patch("builtins.input", return_value="n") as user_input,
|
|
121
|
+
):
|
|
122
|
+
result = app.clear_app_data()
|
|
123
|
+
|
|
124
|
+
self.assertFalse(result)
|
|
125
|
+
user_input.assert_called_once()
|
|
126
|
+
run_command.assert_not_called()
|
|
127
|
+
|
|
128
|
+
def test_outside_project_confirms_before_clearing_foreground_app(self):
|
|
129
|
+
with (
|
|
130
|
+
patch.object(app, "is_flutter_project_root", return_value=False),
|
|
131
|
+
patch.object(
|
|
132
|
+
app,
|
|
133
|
+
"get_current_foreground_app",
|
|
134
|
+
return_value=("android", "com.example.foreground"),
|
|
135
|
+
),
|
|
136
|
+
patch.object(
|
|
137
|
+
app,
|
|
138
|
+
"build_adb_cmd",
|
|
139
|
+
return_value=[
|
|
140
|
+
"adb",
|
|
141
|
+
"shell",
|
|
142
|
+
"pm",
|
|
143
|
+
"clear",
|
|
144
|
+
"com.example.foreground",
|
|
145
|
+
],
|
|
146
|
+
),
|
|
147
|
+
patch.object(app, "run_flutter_command", return_value=True) as run_command,
|
|
148
|
+
patch("builtins.input", return_value="") as user_input,
|
|
149
|
+
):
|
|
150
|
+
result = app.clear_app_data()
|
|
151
|
+
|
|
152
|
+
self.assertTrue(result)
|
|
153
|
+
user_input.assert_called_once()
|
|
154
|
+
run_command.assert_called_once_with(
|
|
155
|
+
["adb", "shell", "pm", "clear", "com.example.foreground"],
|
|
156
|
+
"Clearing app data... ",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
unittest.main()
|
|
@@ -26,6 +26,26 @@ class FdevCliTests(unittest.TestCase):
|
|
|
26
26
|
self.assertEqual(result, 0)
|
|
27
27
|
show_usage.assert_called_once_with(exit_code=0)
|
|
28
28
|
|
|
29
|
+
def test_short_version_flag_prints_installed_version(self):
|
|
30
|
+
with (
|
|
31
|
+
patch.object(sys, "argv", ["fdev", "-v"]),
|
|
32
|
+
patch.object(fdev, "show_fdev_version", return_value=0) as show_version,
|
|
33
|
+
):
|
|
34
|
+
result = fdev.main()
|
|
35
|
+
|
|
36
|
+
self.assertEqual(result, 0)
|
|
37
|
+
show_version.assert_called_once_with()
|
|
38
|
+
|
|
39
|
+
def test_long_version_flag_prints_installed_version(self):
|
|
40
|
+
with (
|
|
41
|
+
patch.object(sys, "argv", ["fdev", "--version"]),
|
|
42
|
+
patch.object(fdev, "show_fdev_version", return_value=0) as show_version,
|
|
43
|
+
):
|
|
44
|
+
result = fdev.main()
|
|
45
|
+
|
|
46
|
+
self.assertEqual(result, 0)
|
|
47
|
+
show_version.assert_called_once_with()
|
|
48
|
+
|
|
29
49
|
def test_unknown_command_prints_usage_and_exits_with_error(self):
|
|
30
50
|
with (
|
|
31
51
|
patch.object(sys, "argv", ["fdev", "unknown"]),
|
|
@@ -2,7 +2,7 @@ import io
|
|
|
2
2
|
import subprocess
|
|
3
3
|
import unittest
|
|
4
4
|
from contextlib import redirect_stdout
|
|
5
|
-
from unittest.mock import patch
|
|
5
|
+
from unittest.mock import Mock, patch
|
|
6
6
|
|
|
7
7
|
from managers import package
|
|
8
8
|
|
|
@@ -38,23 +38,96 @@ class PackageCommandTests(unittest.TestCase):
|
|
|
38
38
|
run.assert_not_called()
|
|
39
39
|
|
|
40
40
|
def test_upgrade_runs_pip_upgrade_in_current_python_environment(self):
|
|
41
|
+
with (
|
|
42
|
+
patch.object(package.platform, "system", return_value="Linux"),
|
|
43
|
+
patch.object(package.sys, "executable", "/venv/bin/python"),
|
|
44
|
+
patch.object(package, "_get_latest_pypi_version", return_value="0.1.13"),
|
|
45
|
+
patch.object(
|
|
46
|
+
package,
|
|
47
|
+
"_get_installed_package_info",
|
|
48
|
+
side_effect=[
|
|
49
|
+
("0.1.12", "/venv/lib/python/site-packages"),
|
|
50
|
+
("0.1.13", "/venv/lib/python/site-packages"),
|
|
51
|
+
],
|
|
52
|
+
),
|
|
53
|
+
patch.object(package, "_run_pip_quiet", return_value=0) as run,
|
|
54
|
+
):
|
|
55
|
+
result = package.upgrade_fdev_command([])
|
|
56
|
+
|
|
57
|
+
self.assertEqual(result, 0)
|
|
58
|
+
run.assert_called_once_with(["install", "--upgrade", "flutter-dev"])
|
|
59
|
+
|
|
60
|
+
def test_upgrade_skips_pip_when_already_up_to_date(self):
|
|
61
|
+
output = io.StringIO()
|
|
62
|
+
|
|
63
|
+
with (
|
|
64
|
+
patch.object(package.platform, "system", return_value="Linux"),
|
|
65
|
+
patch.object(
|
|
66
|
+
package,
|
|
67
|
+
"_get_installed_package_info",
|
|
68
|
+
return_value=("0.1.12", "/venv/lib/python/site-packages"),
|
|
69
|
+
),
|
|
70
|
+
patch.object(package, "_get_latest_pypi_version", return_value="0.1.12"),
|
|
71
|
+
patch.object(package, "_run_pip_quiet") as run,
|
|
72
|
+
redirect_stdout(output),
|
|
73
|
+
):
|
|
74
|
+
result = package.upgrade_fdev_command([])
|
|
75
|
+
|
|
76
|
+
self.assertEqual(result, 0)
|
|
77
|
+
run.assert_not_called()
|
|
78
|
+
self.assertIn("✓ Already up to date: 0.1.12", output.getvalue())
|
|
79
|
+
|
|
80
|
+
def test_quiet_pip_hides_routine_success_output(self):
|
|
41
81
|
completed = subprocess.CompletedProcess(
|
|
42
82
|
["/venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"],
|
|
43
83
|
0,
|
|
84
|
+
stdout="Requirement already satisfied: flutter-dev (0.1.12)\n",
|
|
85
|
+
stderr="",
|
|
44
86
|
)
|
|
87
|
+
output = io.StringIO()
|
|
45
88
|
|
|
46
89
|
with (
|
|
47
|
-
patch.object(package.platform, "system", return_value="Linux"),
|
|
48
90
|
patch.object(package.sys, "executable", "/venv/bin/python"),
|
|
49
91
|
patch.object(package.subprocess, "run", return_value=completed) as run,
|
|
92
|
+
redirect_stdout(output),
|
|
50
93
|
):
|
|
51
|
-
result = package.
|
|
94
|
+
result = package._run_pip_quiet(["install", "--upgrade", "flutter-dev"])
|
|
52
95
|
|
|
53
96
|
self.assertEqual(result, 0)
|
|
97
|
+
self.assertEqual(output.getvalue(), "")
|
|
54
98
|
run.assert_called_once_with(
|
|
55
|
-
["/venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"]
|
|
99
|
+
["/venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"],
|
|
100
|
+
capture_output=True,
|
|
101
|
+
text=True,
|
|
102
|
+
encoding="utf-8",
|
|
103
|
+
errors="replace",
|
|
56
104
|
)
|
|
57
105
|
|
|
106
|
+
def test_latest_pypi_version_uses_package_json_metadata(self):
|
|
107
|
+
response = Mock()
|
|
108
|
+
response.json.return_value = {"info": {"version": "0.1.13"}}
|
|
109
|
+
|
|
110
|
+
with patch.object(package.requests, "get", return_value=response) as get:
|
|
111
|
+
version = package._get_latest_pypi_version()
|
|
112
|
+
|
|
113
|
+
self.assertEqual(version, "0.1.13")
|
|
114
|
+
response.raise_for_status.assert_called_once_with()
|
|
115
|
+
get.assert_called_once_with(
|
|
116
|
+
package.PYPI_JSON_URL,
|
|
117
|
+
headers={"Accept": "application/json"},
|
|
118
|
+
timeout=10,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def test_latest_pypi_version_returns_none_when_lookup_fails(self):
|
|
122
|
+
with patch.object(
|
|
123
|
+
package.requests,
|
|
124
|
+
"get",
|
|
125
|
+
side_effect=package.requests.RequestException("offline"),
|
|
126
|
+
):
|
|
127
|
+
version = package._get_latest_pypi_version()
|
|
128
|
+
|
|
129
|
+
self.assertIsNone(version)
|
|
130
|
+
|
|
58
131
|
def test_windows_delete_schedules_detached_uninstall(self):
|
|
59
132
|
with (
|
|
60
133
|
patch.object(package.platform, "system", return_value="Windows"),
|
|
@@ -100,16 +173,17 @@ class PackageCommandTests(unittest.TestCase):
|
|
|
100
173
|
)
|
|
101
174
|
|
|
102
175
|
def test_upgrade_prints_target_python_environment(self):
|
|
103
|
-
completed = subprocess.CompletedProcess(
|
|
104
|
-
["/home/user/project/.venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"],
|
|
105
|
-
0,
|
|
106
|
-
)
|
|
107
176
|
output = io.StringIO()
|
|
108
177
|
|
|
109
178
|
with (
|
|
110
179
|
patch.object(package.platform, "system", return_value="Linux"),
|
|
111
180
|
patch.object(package.sys, "executable", "/home/user/project/.venv/bin/python"),
|
|
112
|
-
patch.object(
|
|
181
|
+
patch.object(
|
|
182
|
+
package,
|
|
183
|
+
"_get_installed_package_info",
|
|
184
|
+
return_value=("0.1.12", "/home/user/project/.venv/lib/python/site-packages"),
|
|
185
|
+
),
|
|
186
|
+
patch.object(package, "_get_latest_pypi_version", return_value="0.1.12"),
|
|
113
187
|
redirect_stdout(output),
|
|
114
188
|
):
|
|
115
189
|
result = package.upgrade_fdev_command([])
|
|
@@ -118,6 +192,48 @@ class PackageCommandTests(unittest.TestCase):
|
|
|
118
192
|
self.assertIn("Python environment:", output.getvalue())
|
|
119
193
|
self.assertIn("/home/user/project/.venv/bin/python", output.getvalue())
|
|
120
194
|
|
|
195
|
+
def test_successful_upgrade_prints_before_and_after_versions(self):
|
|
196
|
+
output = io.StringIO()
|
|
197
|
+
|
|
198
|
+
with (
|
|
199
|
+
patch.object(package.platform, "system", return_value="Linux"),
|
|
200
|
+
patch.object(
|
|
201
|
+
package,
|
|
202
|
+
"_get_installed_package_info",
|
|
203
|
+
side_effect=[
|
|
204
|
+
("0.1.12", "/venv/lib/python/site-packages"),
|
|
205
|
+
("0.1.13", "/venv/lib/python/site-packages"),
|
|
206
|
+
],
|
|
207
|
+
),
|
|
208
|
+
patch.object(package, "_get_latest_pypi_version", return_value="0.1.13"),
|
|
209
|
+
patch.object(package, "_run_pip_quiet", return_value=0),
|
|
210
|
+
redirect_stdout(output),
|
|
211
|
+
):
|
|
212
|
+
result = package.upgrade_fdev_command([])
|
|
213
|
+
|
|
214
|
+
self.assertEqual(result, 0)
|
|
215
|
+
self.assertIn("0.1.12 → 0.1.13", output.getvalue())
|
|
216
|
+
self.assertIn(
|
|
217
|
+
"Restart your terminal if the fdev command still uses the old version.",
|
|
218
|
+
output.getvalue(),
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def test_show_fdev_version_prints_installed_version(self):
|
|
222
|
+
output = io.StringIO()
|
|
223
|
+
|
|
224
|
+
with (
|
|
225
|
+
patch.object(
|
|
226
|
+
package,
|
|
227
|
+
"_get_installed_package_info",
|
|
228
|
+
return_value=("0.1.13", "/venv/lib/python/site-packages"),
|
|
229
|
+
),
|
|
230
|
+
redirect_stdout(output),
|
|
231
|
+
):
|
|
232
|
+
result = package.show_fdev_version()
|
|
233
|
+
|
|
234
|
+
self.assertEqual(result, 0)
|
|
235
|
+
self.assertEqual(output.getvalue().strip(), "fdev 0.1.13")
|
|
236
|
+
|
|
121
237
|
def test_command_help_returns_zero(self):
|
|
122
238
|
with self.assertRaises(SystemExit) as raised:
|
|
123
239
|
package.delete_fdev_command(["--help"])
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
from unittest.mock import patch
|
|
3
|
-
|
|
4
|
-
from managers import app
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class ClearAppDataTests(unittest.TestCase):
|
|
8
|
-
def test_project_root_clears_project_package_without_confirmation(self):
|
|
9
|
-
with (
|
|
10
|
-
patch.object(app, "is_flutter_project_root", return_value=True),
|
|
11
|
-
patch.object(app, "get_all_connected_devices", return_value=["device-1"]),
|
|
12
|
-
patch.object(app, "ensure_device_connected", return_value=True),
|
|
13
|
-
patch.object(app, "get_package_name", return_value="com.example.app"),
|
|
14
|
-
patch.object(
|
|
15
|
-
app,
|
|
16
|
-
"build_adb_cmd",
|
|
17
|
-
return_value=["adb", "shell", "pm", "clear", "com.example.app"],
|
|
18
|
-
),
|
|
19
|
-
patch.object(app, "run_flutter_command", return_value=True) as run_command,
|
|
20
|
-
patch.object(app, "get_current_foreground_app") as foreground_app,
|
|
21
|
-
patch("builtins.input") as user_input,
|
|
22
|
-
):
|
|
23
|
-
result = app.clear_app_data()
|
|
24
|
-
|
|
25
|
-
self.assertTrue(result)
|
|
26
|
-
foreground_app.assert_not_called()
|
|
27
|
-
user_input.assert_not_called()
|
|
28
|
-
run_command.assert_called_once_with(
|
|
29
|
-
["adb", "shell", "pm", "clear", "com.example.app"],
|
|
30
|
-
"Clearing app data... ",
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
def test_outside_project_confirms_before_clearing_foreground_app(self):
|
|
34
|
-
with (
|
|
35
|
-
patch.object(app, "is_flutter_project_root", return_value=False),
|
|
36
|
-
patch.object(
|
|
37
|
-
app,
|
|
38
|
-
"get_current_foreground_app",
|
|
39
|
-
return_value=("android", "com.example.foreground"),
|
|
40
|
-
),
|
|
41
|
-
patch.object(
|
|
42
|
-
app,
|
|
43
|
-
"build_adb_cmd",
|
|
44
|
-
return_value=[
|
|
45
|
-
"adb",
|
|
46
|
-
"shell",
|
|
47
|
-
"pm",
|
|
48
|
-
"clear",
|
|
49
|
-
"com.example.foreground",
|
|
50
|
-
],
|
|
51
|
-
),
|
|
52
|
-
patch.object(app, "run_flutter_command", return_value=True) as run_command,
|
|
53
|
-
patch("builtins.input", return_value="") as user_input,
|
|
54
|
-
):
|
|
55
|
-
result = app.clear_app_data()
|
|
56
|
-
|
|
57
|
-
self.assertTrue(result)
|
|
58
|
-
user_input.assert_called_once()
|
|
59
|
-
run_command.assert_called_once_with(
|
|
60
|
-
["adb", "shell", "pm", "clear", "com.example.foreground"],
|
|
61
|
-
"Clearing app data... ",
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if __name__ == "__main__":
|
|
66
|
-
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|