fastled 1.2.33__py3-none-any.whl → 1.4.50__py3-none-any.whl
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.
- fastled/__init__.py +51 -192
- fastled/__main__.py +14 -0
- fastled/__version__.py +6 -0
- fastled/app.py +124 -27
- fastled/args.py +124 -0
- fastled/assets/localhost-key.pem +28 -0
- fastled/assets/localhost.pem +27 -0
- fastled/cli.py +10 -2
- fastled/cli_test.py +21 -0
- fastled/cli_test_interactive.py +21 -0
- fastled/client_server.py +334 -55
- fastled/compile_server.py +12 -1
- fastled/compile_server_impl.py +115 -42
- fastled/docker_manager.py +392 -69
- fastled/emoji_util.py +27 -0
- fastled/filewatcher.py +100 -8
- fastled/find_good_connection.py +105 -0
- fastled/header_dump.py +63 -0
- fastled/install/__init__.py +1 -0
- fastled/install/examples_manager.py +62 -0
- fastled/install/extension_manager.py +113 -0
- fastled/install/main.py +156 -0
- fastled/install/project_detection.py +167 -0
- fastled/install/test_install.py +373 -0
- fastled/install/vscode_config.py +344 -0
- fastled/interruptible_http.py +148 -0
- fastled/keyboard.py +1 -0
- fastled/keyz.py +84 -0
- fastled/live_client.py +26 -1
- fastled/open_browser.py +133 -89
- fastled/parse_args.py +219 -15
- fastled/playwright/chrome_extension_downloader.py +207 -0
- fastled/playwright/playwright_browser.py +773 -0
- fastled/playwright/resize_tracking.py +127 -0
- fastled/print_filter.py +52 -0
- fastled/project_init.py +20 -13
- fastled/select_sketch_directory.py +142 -17
- fastled/server_flask.py +487 -0
- fastled/server_start.py +21 -0
- fastled/settings.py +53 -4
- fastled/site/build.py +2 -10
- fastled/site/examples.py +10 -0
- fastled/sketch.py +129 -7
- fastled/string_diff.py +218 -9
- fastled/test/examples.py +7 -5
- fastled/types.py +22 -2
- fastled/util.py +78 -0
- fastled/version.py +41 -0
- fastled/web_compile.py +401 -218
- fastled/zip_files.py +76 -0
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/METADATA +533 -382
- fastled-1.4.50.dist-info/RECORD +60 -0
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/WHEEL +1 -1
- fastled/open_browser2.py +0 -111
- fastled-1.2.33.dist-info/RECORD +0 -33
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/entry_points.txt +0 -0
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info/licenses}/LICENSE +0 -0
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Project detection logic for FastLED installation."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def validate_vscode_project(no_interactive: bool = False) -> bool:
|
|
9
|
+
"""
|
|
10
|
+
Validate if current directory has a VSCode project.
|
|
11
|
+
If not found, search parent directories, offer alternatives.
|
|
12
|
+
Returns True if a VSCode project is found or created.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
no_interactive: If True, fail instead of prompting for input
|
|
16
|
+
"""
|
|
17
|
+
current_dir = Path.cwd()
|
|
18
|
+
|
|
19
|
+
# Check current directory
|
|
20
|
+
if (current_dir / ".vscode").exists():
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
# Search parent directories
|
|
24
|
+
parent_path = find_vscode_project_upward()
|
|
25
|
+
if parent_path:
|
|
26
|
+
if no_interactive:
|
|
27
|
+
print("❌ No .vscode directory found in current directory.")
|
|
28
|
+
print(f" Found .vscode in parent: {parent_path}")
|
|
29
|
+
print(" In non-interactive mode, cannot change directory.")
|
|
30
|
+
print(f" Please cd to {parent_path} and run the command again.")
|
|
31
|
+
return False
|
|
32
|
+
answer = (
|
|
33
|
+
input(f"Found a .vscode project in {parent_path}/\nInstall there? [y/n] ")
|
|
34
|
+
.strip()
|
|
35
|
+
.lower()
|
|
36
|
+
)
|
|
37
|
+
if answer in ["y", "yes"]:
|
|
38
|
+
import os
|
|
39
|
+
|
|
40
|
+
os.chdir(parent_path)
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
# Check if IDE is available
|
|
44
|
+
if not (shutil.which("code") or shutil.which("cursor")):
|
|
45
|
+
print(
|
|
46
|
+
"No supported IDE found (VSCode or Cursor). Please install VSCode or Cursor first."
|
|
47
|
+
)
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
# Offer to create new project
|
|
51
|
+
if no_interactive:
|
|
52
|
+
print(
|
|
53
|
+
"❌ No .vscode directory found in current directory or parent directories."
|
|
54
|
+
)
|
|
55
|
+
print(" In non-interactive mode, cannot create new project.")
|
|
56
|
+
print(" Please create a .vscode directory or run without --no-interactive.")
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
print("No .vscode directory found in current directory or parent directories.")
|
|
60
|
+
answer = (
|
|
61
|
+
input(
|
|
62
|
+
"Would you like to generate a VSCode project with FastLED configuration? [y/n] "
|
|
63
|
+
)
|
|
64
|
+
.strip()
|
|
65
|
+
.lower()
|
|
66
|
+
)
|
|
67
|
+
if answer in ["y", "yes"]:
|
|
68
|
+
return generate_vscode_project()
|
|
69
|
+
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def find_vscode_project_upward(max_levels: int = 5) -> Path | None:
|
|
74
|
+
"""Search parent directories for .vscode folder."""
|
|
75
|
+
current = Path.cwd()
|
|
76
|
+
|
|
77
|
+
for _ in range(max_levels):
|
|
78
|
+
parent = current.parent
|
|
79
|
+
if parent == current: # Reached root
|
|
80
|
+
break
|
|
81
|
+
current = parent
|
|
82
|
+
if (current / ".vscode").exists():
|
|
83
|
+
return current
|
|
84
|
+
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def generate_vscode_project() -> bool:
|
|
89
|
+
"""Create a new .vscode directory structure."""
|
|
90
|
+
vscode_dir = Path.cwd() / ".vscode"
|
|
91
|
+
vscode_dir.mkdir(exist_ok=True)
|
|
92
|
+
print(f"✅ Created .vscode directory at {vscode_dir}")
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def detect_fastled_project() -> bool:
|
|
97
|
+
"""Check if library.json contains FastLED."""
|
|
98
|
+
library_json = Path.cwd() / "library.json"
|
|
99
|
+
if not library_json.exists():
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
with open(library_json, "r") as f:
|
|
104
|
+
data = json.load(f)
|
|
105
|
+
return data.get("name") == "FastLED"
|
|
106
|
+
except (json.JSONDecodeError, KeyError):
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def is_fastled_repository() -> bool:
|
|
111
|
+
"""
|
|
112
|
+
🚨 CRITICAL: Detect actual FastLED repository.
|
|
113
|
+
Strict verification of multiple markers.
|
|
114
|
+
"""
|
|
115
|
+
cwd = Path.cwd()
|
|
116
|
+
|
|
117
|
+
# Required files and directories for FastLED repository
|
|
118
|
+
required_markers = [
|
|
119
|
+
cwd / "src" / "FastLED.h",
|
|
120
|
+
cwd / "examples" / "Blink" / "Blink.ino",
|
|
121
|
+
cwd / "ci" / "ci-compile.py",
|
|
122
|
+
cwd / "src" / "platforms",
|
|
123
|
+
cwd / "library.json",
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
# Check all markers exist
|
|
127
|
+
if not all(marker.exists() for marker in required_markers):
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
# Verify library.json has correct content
|
|
131
|
+
try:
|
|
132
|
+
with open(cwd / "library.json", "r") as f:
|
|
133
|
+
data = json.load(f)
|
|
134
|
+
if data.get("name") != "FastLED":
|
|
135
|
+
return False
|
|
136
|
+
repo_url = data.get("repository", {}).get("url", "")
|
|
137
|
+
if "FastLED/FastLED" not in repo_url:
|
|
138
|
+
return False
|
|
139
|
+
except (json.JSONDecodeError, KeyError, FileNotFoundError):
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
# Check for test files pattern
|
|
143
|
+
test_dir = cwd / "tests"
|
|
144
|
+
if test_dir.exists() and test_dir.is_dir():
|
|
145
|
+
test_files = list(test_dir.glob("test_*.cpp"))
|
|
146
|
+
if not test_files:
|
|
147
|
+
return False
|
|
148
|
+
else:
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def check_existing_arduino_content() -> bool:
|
|
155
|
+
"""Check for .ino files OR examples/ folder."""
|
|
156
|
+
cwd = Path.cwd()
|
|
157
|
+
|
|
158
|
+
# Check for any .ino files
|
|
159
|
+
ino_files = list(cwd.rglob("*.ino"))
|
|
160
|
+
if ino_files:
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
# Check for examples folder
|
|
164
|
+
if (cwd / "examples").exists():
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
return False
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"""Comprehensive test suite for FastLED install feature."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import unittest
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from unittest.mock import MagicMock, patch
|
|
11
|
+
|
|
12
|
+
# Add parent directory to path for imports
|
|
13
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
14
|
+
|
|
15
|
+
from fastled.install.main import auto_execute_fastled, fastled_install
|
|
16
|
+
from fastled.install.project_detection import (
|
|
17
|
+
check_existing_arduino_content,
|
|
18
|
+
)
|
|
19
|
+
from fastled.install.vscode_config import (
|
|
20
|
+
generate_fastled_tasks,
|
|
21
|
+
update_launch_json_for_arduino,
|
|
22
|
+
update_vscode_settings_for_fastled,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TestFastLEDInstall(unittest.TestCase):
|
|
27
|
+
"""Test FastLED installation functionality."""
|
|
28
|
+
|
|
29
|
+
def setUp(self):
|
|
30
|
+
"""Set up test environment."""
|
|
31
|
+
self.test_dir = tempfile.mkdtemp()
|
|
32
|
+
self.original_cwd = os.getcwd()
|
|
33
|
+
os.chdir(self.test_dir)
|
|
34
|
+
|
|
35
|
+
def tearDown(self):
|
|
36
|
+
"""Clean up test environment."""
|
|
37
|
+
os.chdir(self.original_cwd)
|
|
38
|
+
shutil.rmtree(self.test_dir)
|
|
39
|
+
|
|
40
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
41
|
+
@patch("builtins.input")
|
|
42
|
+
def test_dry_run_basic_project(self, mock_input, mock_which):
|
|
43
|
+
"""Test 1: Dry-run in basic project."""
|
|
44
|
+
# Setup
|
|
45
|
+
mock_which.return_value = "/usr/bin/code"
|
|
46
|
+
mock_input.return_value = "y"
|
|
47
|
+
os.makedirs(".vscode")
|
|
48
|
+
|
|
49
|
+
# Run
|
|
50
|
+
result = fastled_install(dry_run=True, no_interactive=True)
|
|
51
|
+
|
|
52
|
+
# Verify
|
|
53
|
+
self.assertTrue(result)
|
|
54
|
+
self.assertTrue(Path(".vscode/launch.json").exists())
|
|
55
|
+
self.assertTrue(Path(".vscode/tasks.json").exists())
|
|
56
|
+
|
|
57
|
+
@patch("fastled.install.project_detection.is_fastled_repository")
|
|
58
|
+
@patch("fastled.install.project_detection.detect_fastled_project")
|
|
59
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
60
|
+
@patch("builtins.input")
|
|
61
|
+
def test_dry_run_fastled_external(
|
|
62
|
+
self, mock_input, mock_which, mock_detect, mock_repo
|
|
63
|
+
):
|
|
64
|
+
"""Test 2: Dry-run in external FastLED project."""
|
|
65
|
+
# Setup
|
|
66
|
+
mock_which.return_value = "/usr/bin/code"
|
|
67
|
+
mock_input.return_value = "y"
|
|
68
|
+
mock_detect.return_value = True
|
|
69
|
+
mock_repo.return_value = False
|
|
70
|
+
os.makedirs(".vscode")
|
|
71
|
+
|
|
72
|
+
# Create library.json
|
|
73
|
+
with open("library.json", "w") as f:
|
|
74
|
+
json.dump({"name": "FastLED"}, f)
|
|
75
|
+
|
|
76
|
+
# Run
|
|
77
|
+
result = fastled_install(dry_run=True, no_interactive=True)
|
|
78
|
+
|
|
79
|
+
# Verify
|
|
80
|
+
self.assertTrue(result)
|
|
81
|
+
self.assertFalse(Path(".vscode/settings.json").exists()) # No clangd settings
|
|
82
|
+
|
|
83
|
+
@patch("fastled.install.project_detection.is_fastled_repository")
|
|
84
|
+
@patch("fastled.install.project_detection.detect_fastled_project")
|
|
85
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
86
|
+
@patch("builtins.input")
|
|
87
|
+
def test_dry_run_fastled_repository(
|
|
88
|
+
self, mock_input, mock_which, mock_detect, mock_repo
|
|
89
|
+
):
|
|
90
|
+
"""Test 3: Dry-run in actual FastLED repository."""
|
|
91
|
+
# Setup
|
|
92
|
+
mock_which.return_value = "/usr/bin/code"
|
|
93
|
+
mock_input.return_value = "y"
|
|
94
|
+
mock_detect.return_value = True
|
|
95
|
+
mock_repo.return_value = True
|
|
96
|
+
os.makedirs(".vscode")
|
|
97
|
+
|
|
98
|
+
# Run
|
|
99
|
+
result = fastled_install(dry_run=True, no_interactive=True)
|
|
100
|
+
|
|
101
|
+
# Verify
|
|
102
|
+
self.assertTrue(result)
|
|
103
|
+
self.assertTrue(Path(".vscode/settings.json").exists()) # Has clangd settings
|
|
104
|
+
|
|
105
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
106
|
+
@patch("builtins.input")
|
|
107
|
+
def test_existing_vscode_project(self, mock_input, mock_which):
|
|
108
|
+
"""Test 4: Merge with existing .vscode configs."""
|
|
109
|
+
# Setup
|
|
110
|
+
mock_which.return_value = "/usr/bin/code"
|
|
111
|
+
mock_input.return_value = "y"
|
|
112
|
+
os.makedirs(".vscode")
|
|
113
|
+
|
|
114
|
+
# Create existing launch.json
|
|
115
|
+
existing_config = {
|
|
116
|
+
"version": "0.2.0",
|
|
117
|
+
"configurations": [{"name": "Existing", "type": "node"}],
|
|
118
|
+
}
|
|
119
|
+
with open(".vscode/launch.json", "w") as f:
|
|
120
|
+
json.dump(existing_config, f)
|
|
121
|
+
|
|
122
|
+
# Run
|
|
123
|
+
result = fastled_install(dry_run=True, no_interactive=True)
|
|
124
|
+
|
|
125
|
+
# Verify
|
|
126
|
+
self.assertTrue(result)
|
|
127
|
+
with open(".vscode/launch.json") as f:
|
|
128
|
+
data = json.load(f)
|
|
129
|
+
self.assertEqual(len(data["configurations"]), 2)
|
|
130
|
+
self.assertEqual(
|
|
131
|
+
data["configurations"][0]["name"],
|
|
132
|
+
"🎯 Auto Debug (Smart File Detection)",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
136
|
+
@patch("builtins.input")
|
|
137
|
+
def test_parent_directory_detection(self, mock_input, mock_which):
|
|
138
|
+
"""Test 5: Find .vscode in parent directories."""
|
|
139
|
+
# Setup
|
|
140
|
+
mock_which.return_value = "/usr/bin/code"
|
|
141
|
+
parent_dir = Path(self.test_dir)
|
|
142
|
+
child_dir = parent_dir / "child" / "grandchild"
|
|
143
|
+
child_dir.mkdir(parents=True)
|
|
144
|
+
(parent_dir / ".vscode").mkdir()
|
|
145
|
+
os.chdir(child_dir)
|
|
146
|
+
|
|
147
|
+
# Test non-interactive mode - should fail
|
|
148
|
+
result = fastled_install(dry_run=True, no_interactive=True)
|
|
149
|
+
self.assertFalse(result) # Should fail in non-interactive
|
|
150
|
+
|
|
151
|
+
# Test interactive mode
|
|
152
|
+
mock_input.side_effect = ["y", "y"] # Yes to parent, yes to extension
|
|
153
|
+
result = fastled_install(dry_run=True, no_interactive=False)
|
|
154
|
+
|
|
155
|
+
# Verify - we should be in parent directory now
|
|
156
|
+
self.assertTrue(result)
|
|
157
|
+
self.assertEqual(Path.cwd(), parent_dir)
|
|
158
|
+
|
|
159
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
160
|
+
@patch("builtins.input")
|
|
161
|
+
def test_project_generation(self, mock_input, mock_which):
|
|
162
|
+
"""Test 6: Generate new VSCode project."""
|
|
163
|
+
# Setup
|
|
164
|
+
mock_which.return_value = "/usr/bin/code"
|
|
165
|
+
|
|
166
|
+
# Test non-interactive mode - should fail
|
|
167
|
+
result = fastled_install(dry_run=True, no_interactive=True)
|
|
168
|
+
self.assertFalse(result) # Should fail without .vscode
|
|
169
|
+
|
|
170
|
+
# Test interactive mode
|
|
171
|
+
mock_input.side_effect = ["y", "y"] # Yes to generate, yes to extension
|
|
172
|
+
result = fastled_install(dry_run=True, no_interactive=False)
|
|
173
|
+
|
|
174
|
+
# Verify
|
|
175
|
+
self.assertTrue(result)
|
|
176
|
+
self.assertTrue(Path(".vscode").exists())
|
|
177
|
+
self.assertTrue(Path(".vscode/launch.json").exists())
|
|
178
|
+
self.assertTrue(Path(".vscode/tasks.json").exists())
|
|
179
|
+
|
|
180
|
+
def test_arduino_content_detection(self):
|
|
181
|
+
"""Test 7: Detect existing .ino files."""
|
|
182
|
+
# Create .ino file
|
|
183
|
+
with open("test.ino", "w") as f:
|
|
184
|
+
f.write("void setup() {}")
|
|
185
|
+
|
|
186
|
+
# Test detection
|
|
187
|
+
self.assertTrue(check_existing_arduino_content())
|
|
188
|
+
|
|
189
|
+
# Remove and test examples folder
|
|
190
|
+
os.unlink("test.ino")
|
|
191
|
+
os.makedirs("examples")
|
|
192
|
+
self.assertTrue(check_existing_arduino_content())
|
|
193
|
+
|
|
194
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
195
|
+
@patch("builtins.input")
|
|
196
|
+
def test_tasks_json_merging(self, mock_input, mock_which):
|
|
197
|
+
"""Test 8: Merge FastLED tasks with existing."""
|
|
198
|
+
# Setup
|
|
199
|
+
mock_which.return_value = "/usr/bin/code"
|
|
200
|
+
mock_input.return_value = "y"
|
|
201
|
+
os.makedirs(".vscode")
|
|
202
|
+
|
|
203
|
+
# Create existing tasks.json
|
|
204
|
+
existing_tasks = {
|
|
205
|
+
"version": "2.0.0",
|
|
206
|
+
"tasks": [{"label": "Existing Task", "command": "echo"}],
|
|
207
|
+
}
|
|
208
|
+
with open(".vscode/tasks.json", "w") as f:
|
|
209
|
+
json.dump(existing_tasks, f)
|
|
210
|
+
|
|
211
|
+
# Run
|
|
212
|
+
generate_fastled_tasks()
|
|
213
|
+
|
|
214
|
+
# Verify
|
|
215
|
+
with open(".vscode/tasks.json") as f:
|
|
216
|
+
data = json.load(f)
|
|
217
|
+
self.assertEqual(len(data["tasks"]), 3) # 1 existing + 2 FastLED
|
|
218
|
+
labels = [task["label"] for task in data["tasks"]]
|
|
219
|
+
self.assertIn("Run FastLED (Debug)", labels)
|
|
220
|
+
self.assertIn("Run FastLED (Quick)", labels)
|
|
221
|
+
|
|
222
|
+
def test_launch_json_updates(self):
|
|
223
|
+
"""Test 9: Update launch.json configurations."""
|
|
224
|
+
# Setup
|
|
225
|
+
os.makedirs(".vscode")
|
|
226
|
+
|
|
227
|
+
# Run
|
|
228
|
+
update_launch_json_for_arduino()
|
|
229
|
+
|
|
230
|
+
# Verify
|
|
231
|
+
with open(".vscode/launch.json") as f:
|
|
232
|
+
data = json.load(f)
|
|
233
|
+
self.assertEqual(len(data["configurations"]), 1)
|
|
234
|
+
config = data["configurations"][0]
|
|
235
|
+
self.assertEqual(config["name"], "🎯 Auto Debug (Smart File Detection)")
|
|
236
|
+
self.assertEqual(config["type"], "auto-debug")
|
|
237
|
+
self.assertIn("*.ino", config["map"])
|
|
238
|
+
|
|
239
|
+
@patch("fastled.install.project_detection.is_fastled_repository")
|
|
240
|
+
def test_safety_clangd_protection(self, mock_repo):
|
|
241
|
+
"""Test 10: 🚨 CRITICAL - clangd safety protection."""
|
|
242
|
+
# Setup
|
|
243
|
+
os.makedirs(".vscode")
|
|
244
|
+
|
|
245
|
+
# Test non-repository
|
|
246
|
+
mock_repo.return_value = False
|
|
247
|
+
update_vscode_settings_for_fastled()
|
|
248
|
+
self.assertFalse(Path(".vscode/settings.json").exists())
|
|
249
|
+
|
|
250
|
+
# Test repository
|
|
251
|
+
mock_repo.return_value = True
|
|
252
|
+
update_vscode_settings_for_fastled()
|
|
253
|
+
self.assertTrue(Path(".vscode/settings.json").exists())
|
|
254
|
+
|
|
255
|
+
with open(".vscode/settings.json") as f:
|
|
256
|
+
data = json.load(f)
|
|
257
|
+
self.assertIn("clangd.arguments", data)
|
|
258
|
+
|
|
259
|
+
@patch("fastled.install.main.check_existing_arduino_content")
|
|
260
|
+
def test_auto_execution_trigger(self, mock_check):
|
|
261
|
+
"""Test 11: Post-installation auto-execution."""
|
|
262
|
+
# Setup
|
|
263
|
+
mock_check.return_value = True
|
|
264
|
+
original_argv = sys.argv.copy()
|
|
265
|
+
sys.argv = ["fastled", "--install"]
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
# We'll test that auto_execute_fastled modifies sys.argv correctly
|
|
269
|
+
# without actually calling main()
|
|
270
|
+
with patch("fastled.app.main") as mock_main:
|
|
271
|
+
mock_main.return_value = 0
|
|
272
|
+
auto_execute_fastled()
|
|
273
|
+
|
|
274
|
+
# Verify
|
|
275
|
+
mock_main.assert_called_once()
|
|
276
|
+
# Check that argv was filtered before calling main
|
|
277
|
+
# The function should have set sys.argv to ['fastled', '.']
|
|
278
|
+
finally:
|
|
279
|
+
sys.argv = original_argv
|
|
280
|
+
|
|
281
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
282
|
+
def test_no_ide_error_handling(self, mock_which):
|
|
283
|
+
"""Test 12: Error when no IDE available."""
|
|
284
|
+
# Setup
|
|
285
|
+
mock_which.return_value = None
|
|
286
|
+
|
|
287
|
+
# Run
|
|
288
|
+
from fastled.install.project_detection import validate_vscode_project
|
|
289
|
+
|
|
290
|
+
result = validate_vscode_project(no_interactive=True)
|
|
291
|
+
|
|
292
|
+
# Verify
|
|
293
|
+
self.assertFalse(result)
|
|
294
|
+
|
|
295
|
+
@patch("subprocess.run")
|
|
296
|
+
@patch("builtins.input")
|
|
297
|
+
def test_examples_installation(self, mock_input, mock_run):
|
|
298
|
+
"""Test 13: --project-init examples installation."""
|
|
299
|
+
# Setup
|
|
300
|
+
mock_input.return_value = "y"
|
|
301
|
+
mock_run.return_value = MagicMock(returncode=0)
|
|
302
|
+
|
|
303
|
+
# Run
|
|
304
|
+
from fastled.install.examples_manager import (
|
|
305
|
+
install_fastled_examples_via_project_init,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# Test non-interactive mode - should skip
|
|
309
|
+
result = install_fastled_examples_via_project_init(no_interactive=True)
|
|
310
|
+
self.assertFalse(result)
|
|
311
|
+
|
|
312
|
+
# Test interactive mode
|
|
313
|
+
result = install_fastled_examples_via_project_init(no_interactive=False)
|
|
314
|
+
self.assertTrue(result)
|
|
315
|
+
mock_run.assert_called_once()
|
|
316
|
+
call_args = mock_run.call_args[0][0]
|
|
317
|
+
self.assertEqual(call_args, ["fastled", "--project-init"])
|
|
318
|
+
|
|
319
|
+
@patch("fastled.install.extension_manager.download_auto_debug_extension")
|
|
320
|
+
@patch("fastled.install.extension_manager.install_vscode_extensions")
|
|
321
|
+
def test_extension_installation_flow(self, mock_install, mock_download):
|
|
322
|
+
"""Test 14: Auto Debug extension prompt/install."""
|
|
323
|
+
# Setup
|
|
324
|
+
mock_download.return_value = Path("test.vsix")
|
|
325
|
+
mock_install.return_value = True
|
|
326
|
+
|
|
327
|
+
# Test dry run
|
|
328
|
+
from fastled.install.extension_manager import install_auto_debug_extension
|
|
329
|
+
|
|
330
|
+
result = install_auto_debug_extension(dry_run=True)
|
|
331
|
+
self.assertTrue(result)
|
|
332
|
+
mock_download.assert_not_called()
|
|
333
|
+
|
|
334
|
+
# Test real install
|
|
335
|
+
result = install_auto_debug_extension(dry_run=False)
|
|
336
|
+
self.assertTrue(result)
|
|
337
|
+
mock_download.assert_called_once()
|
|
338
|
+
mock_install.assert_called_once()
|
|
339
|
+
|
|
340
|
+
@patch("fastled.install.project_detection.is_fastled_repository")
|
|
341
|
+
@patch("fastled.install.project_detection.shutil.which")
|
|
342
|
+
@patch("builtins.input")
|
|
343
|
+
def test_comprehensive_integration(self, mock_input, mock_which, mock_repo):
|
|
344
|
+
"""Test 15: End-to-end integration test."""
|
|
345
|
+
# Setup
|
|
346
|
+
mock_which.return_value = "/usr/bin/code"
|
|
347
|
+
mock_input.side_effect = ["y", "y", "y"] # Yes to project, extension, examples
|
|
348
|
+
mock_repo.return_value = False
|
|
349
|
+
|
|
350
|
+
# Create .vscode first for non-interactive test
|
|
351
|
+
os.makedirs(".vscode")
|
|
352
|
+
|
|
353
|
+
# Run full installation in non-interactive mode
|
|
354
|
+
result = fastled_install(dry_run=True, no_interactive=True)
|
|
355
|
+
|
|
356
|
+
# Verify all components
|
|
357
|
+
self.assertTrue(result)
|
|
358
|
+
self.assertTrue(Path(".vscode").exists())
|
|
359
|
+
self.assertTrue(Path(".vscode/launch.json").exists())
|
|
360
|
+
self.assertTrue(Path(".vscode/tasks.json").exists())
|
|
361
|
+
|
|
362
|
+
# Verify tasks have correct content
|
|
363
|
+
with open(".vscode/tasks.json") as f:
|
|
364
|
+
data = json.load(f)
|
|
365
|
+
debug_task = next(
|
|
366
|
+
t for t in data["tasks"] if t["label"] == "Run FastLED (Debug)"
|
|
367
|
+
)
|
|
368
|
+
self.assertIn("--debug", debug_task["args"])
|
|
369
|
+
self.assertIn("--app", debug_task["args"])
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
if __name__ == "__main__":
|
|
373
|
+
unittest.main()
|