ios-unittest-generator 4.19.3__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.
- ios_unittest_generator-4.19.3/.gitignore +23 -0
- ios_unittest_generator-4.19.3/LICENSE +28 -0
- ios_unittest_generator-4.19.3/PKG-INFO +115 -0
- ios_unittest_generator-4.19.3/README.md +96 -0
- ios_unittest_generator-4.19.3/pyproject.toml +49 -0
- ios_unittest_generator-4.19.3/src/ios_unittest_generator/__init__.py +15 -0
- ios_unittest_generator-4.19.3/src/ios_unittest_generator/__main__.py +11 -0
- ios_unittest_generator-4.19.3/src/ios_unittest_generator/build_file_utils.py +293 -0
- ios_unittest_generator-4.19.3/src/ios_unittest_generator/config.py +212 -0
- ios_unittest_generator-4.19.3/src/ios_unittest_generator/ios_test_patterns.json +1131 -0
- ios_unittest_generator-4.19.3/src/ios_unittest_generator/server.py +7412 -0
- ios_unittest_generator-4.19.3/src/ios_unittest_generator/workflow_state.py +156 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ios-unittest-generator
|
|
3
|
+
Version: 4.19.3
|
|
4
|
+
Summary: MCP Server for generating iOS unit tests for Chromium/Edge projects
|
|
5
|
+
Author: Microsoft Edge iOS Team
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: chromium,edge,ios,mcp,test-generation,unittest
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Testing
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: mcp<2.0.0,>=1.9.4
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# iOS Unit Test Generator
|
|
21
|
+
|
|
22
|
+
智能 MCP 服务器,自动生成、编译、运行和修复 iOS 单元测试。
|
|
23
|
+
|
|
24
|
+
## 🚀 快速开始
|
|
25
|
+
|
|
26
|
+
**无需拉取代码,直接在 VS Code 中配置即可使用!**
|
|
27
|
+
|
|
28
|
+
### 方式一:VS Code 一键安装(最简单 ⭐)
|
|
29
|
+
|
|
30
|
+
1. 打开 VS Code
|
|
31
|
+
2. `Cmd+Shift+P` → 输入 `MCP: Add Server`
|
|
32
|
+
3. 选择 **"Pip Package"**
|
|
33
|
+
4. 输入包名:`ios-unittest-generator`
|
|
34
|
+
5. 设置环境变量 `CHROMIUM_SRC` 为你的 Chromium 源码路径
|
|
35
|
+
6. 完成!
|
|
36
|
+
|
|
37
|
+
### 方式二:手动编辑配置文件
|
|
38
|
+
|
|
39
|
+
**macOS/Linux** - 编辑 `~/.vscode/mcp.json`:
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"servers": {
|
|
43
|
+
"ios-unittest-generator": {
|
|
44
|
+
"command": "uvx",
|
|
45
|
+
"args": ["ios-unittest-generator"],
|
|
46
|
+
"env": {
|
|
47
|
+
"CHROMIUM_SRC": "/path/to/chromium/src"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Windows** - 编辑 `%APPDATA%\Code\User\mcp.json`:
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"servers": {
|
|
58
|
+
"ios-unittest-generator": {
|
|
59
|
+
"command": "uvx",
|
|
60
|
+
"args": ["ios-unittest-generator"],
|
|
61
|
+
"env": {
|
|
62
|
+
"CHROMIUM_SRC": "C:\\path\\to\\chromium\\src"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 📋 核心功能
|
|
70
|
+
|
|
71
|
+
- ✅ 自动分析源文件,识别可测试元素
|
|
72
|
+
- ✅ 生成完整的测试文件(包含 fixture、SetUp、测试用例)
|
|
73
|
+
- ✅ 智能检测测试目标(支持 `ios/chrome/*`、`components/*/ios/*` 等所有路径)
|
|
74
|
+
- ✅ 自动更新 BUILD.gn 文件(按字母顺序)
|
|
75
|
+
- ✅ 自动编译测试,智能分析编译错误
|
|
76
|
+
- ✅ 自动运行测试,智能分析运行时错误
|
|
77
|
+
|
|
78
|
+
## 📖 11 个 MCP 工具
|
|
79
|
+
|
|
80
|
+
| 工具 | 功能 |
|
|
81
|
+
|------|------|
|
|
82
|
+
| `full_test_workflow` | 完整工作流(分析→生成→增强→编译→运行) |
|
|
83
|
+
| `analyze_ios_code_for_testing` | 分析源文件,提取可测试元素 |
|
|
84
|
+
| `generate_ios_unittest_file` | 生成测试文件 |
|
|
85
|
+
| `check_ios_test_coverage` | 检查测试覆盖率 |
|
|
86
|
+
| `verify_test_enhancement_complete` | 验证测试增强完成(质量门控) |
|
|
87
|
+
| `compile_ios_unittest` | 编译测试(自动错误分析) |
|
|
88
|
+
| `run_ios_unittest` | 运行测试(自动错误分析) |
|
|
89
|
+
| `analyze_runtime_errors` | 分析运行时错误 |
|
|
90
|
+
| `analyze_compilation_errors` | 分析编译错误 |
|
|
91
|
+
| `update_existing_tests` | 增量更新测试 |
|
|
92
|
+
| `update_build_file_for_test` | 自动更新 BUILD 文件 |
|
|
93
|
+
|
|
94
|
+
## 💡 使用示例
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# 一键生成完整测试
|
|
98
|
+
Use full_test_workflow for ios/chrome/browser/ui/foo.mm
|
|
99
|
+
|
|
100
|
+
# 单独编译
|
|
101
|
+
Use compile_ios_unittest for ios/chrome/browser/ui/foo.mm
|
|
102
|
+
|
|
103
|
+
# 单独运行
|
|
104
|
+
Use run_ios_unittest with filter FooTest.*
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 🔗 相关链接
|
|
108
|
+
|
|
109
|
+
- **PyPI**: https://pypi.org/project/ios-unittest-generator/
|
|
110
|
+
- **快速入门**: [QUICKSTART.md](QUICKSTART.md)
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
**版本**: v4.19.1
|
|
115
|
+
**更新日期**: 2026-01-22
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# iOS Unit Test Generator
|
|
2
|
+
|
|
3
|
+
智能 MCP 服务器,自动生成、编译、运行和修复 iOS 单元测试。
|
|
4
|
+
|
|
5
|
+
## 🚀 快速开始
|
|
6
|
+
|
|
7
|
+
**无需拉取代码,直接在 VS Code 中配置即可使用!**
|
|
8
|
+
|
|
9
|
+
### 方式一:VS Code 一键安装(最简单 ⭐)
|
|
10
|
+
|
|
11
|
+
1. 打开 VS Code
|
|
12
|
+
2. `Cmd+Shift+P` → 输入 `MCP: Add Server`
|
|
13
|
+
3. 选择 **"Pip Package"**
|
|
14
|
+
4. 输入包名:`ios-unittest-generator`
|
|
15
|
+
5. 设置环境变量 `CHROMIUM_SRC` 为你的 Chromium 源码路径
|
|
16
|
+
6. 完成!
|
|
17
|
+
|
|
18
|
+
### 方式二:手动编辑配置文件
|
|
19
|
+
|
|
20
|
+
**macOS/Linux** - 编辑 `~/.vscode/mcp.json`:
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"servers": {
|
|
24
|
+
"ios-unittest-generator": {
|
|
25
|
+
"command": "uvx",
|
|
26
|
+
"args": ["ios-unittest-generator"],
|
|
27
|
+
"env": {
|
|
28
|
+
"CHROMIUM_SRC": "/path/to/chromium/src"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Windows** - 编辑 `%APPDATA%\Code\User\mcp.json`:
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"servers": {
|
|
39
|
+
"ios-unittest-generator": {
|
|
40
|
+
"command": "uvx",
|
|
41
|
+
"args": ["ios-unittest-generator"],
|
|
42
|
+
"env": {
|
|
43
|
+
"CHROMIUM_SRC": "C:\\path\\to\\chromium\\src"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 📋 核心功能
|
|
51
|
+
|
|
52
|
+
- ✅ 自动分析源文件,识别可测试元素
|
|
53
|
+
- ✅ 生成完整的测试文件(包含 fixture、SetUp、测试用例)
|
|
54
|
+
- ✅ 智能检测测试目标(支持 `ios/chrome/*`、`components/*/ios/*` 等所有路径)
|
|
55
|
+
- ✅ 自动更新 BUILD.gn 文件(按字母顺序)
|
|
56
|
+
- ✅ 自动编译测试,智能分析编译错误
|
|
57
|
+
- ✅ 自动运行测试,智能分析运行时错误
|
|
58
|
+
|
|
59
|
+
## 📖 11 个 MCP 工具
|
|
60
|
+
|
|
61
|
+
| 工具 | 功能 |
|
|
62
|
+
|------|------|
|
|
63
|
+
| `full_test_workflow` | 完整工作流(分析→生成→增强→编译→运行) |
|
|
64
|
+
| `analyze_ios_code_for_testing` | 分析源文件,提取可测试元素 |
|
|
65
|
+
| `generate_ios_unittest_file` | 生成测试文件 |
|
|
66
|
+
| `check_ios_test_coverage` | 检查测试覆盖率 |
|
|
67
|
+
| `verify_test_enhancement_complete` | 验证测试增强完成(质量门控) |
|
|
68
|
+
| `compile_ios_unittest` | 编译测试(自动错误分析) |
|
|
69
|
+
| `run_ios_unittest` | 运行测试(自动错误分析) |
|
|
70
|
+
| `analyze_runtime_errors` | 分析运行时错误 |
|
|
71
|
+
| `analyze_compilation_errors` | 分析编译错误 |
|
|
72
|
+
| `update_existing_tests` | 增量更新测试 |
|
|
73
|
+
| `update_build_file_for_test` | 自动更新 BUILD 文件 |
|
|
74
|
+
|
|
75
|
+
## 💡 使用示例
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# 一键生成完整测试
|
|
79
|
+
Use full_test_workflow for ios/chrome/browser/ui/foo.mm
|
|
80
|
+
|
|
81
|
+
# 单独编译
|
|
82
|
+
Use compile_ios_unittest for ios/chrome/browser/ui/foo.mm
|
|
83
|
+
|
|
84
|
+
# 单独运行
|
|
85
|
+
Use run_ios_unittest with filter FooTest.*
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 🔗 相关链接
|
|
89
|
+
|
|
90
|
+
- **PyPI**: https://pypi.org/project/ios-unittest-generator/
|
|
91
|
+
- **快速入门**: [QUICKSTART.md](QUICKSTART.md)
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
**版本**: v4.19.1
|
|
96
|
+
**更新日期**: 2026-01-22
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ios-unittest-generator"
|
|
7
|
+
version = "4.19.3"
|
|
8
|
+
description = "MCP Server for generating iOS unit tests for Chromium/Edge projects"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "BSD-3-Clause"
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Microsoft Edge iOS Team" }
|
|
13
|
+
]
|
|
14
|
+
keywords = ["mcp", "ios", "unittest", "chromium", "edge", "test-generation"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: BSD License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Software Development :: Testing",
|
|
23
|
+
]
|
|
24
|
+
requires-python = ">=3.11"
|
|
25
|
+
dependencies = [
|
|
26
|
+
"mcp>=1.9.4,<2.0.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.scripts]
|
|
30
|
+
ios-unittest-generator = "ios_unittest_generator:main"
|
|
31
|
+
ios_unittest_generator = "ios_unittest_generator:main"
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
# Update these URLs when publishing to your actual repository
|
|
35
|
+
# Homepage = "https://github.com/your-org/ios-unittest-generator"
|
|
36
|
+
# Repository = "https://github.com/your-org/ios-unittest-generator"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/ios_unittest_generator"]
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
42
|
+
"src/ios_unittest_generator/ios_test_patterns.json" = "ios_unittest_generator/ios_test_patterns.json"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.sdist]
|
|
45
|
+
include = [
|
|
46
|
+
"src/",
|
|
47
|
+
"README.md",
|
|
48
|
+
"LICENSE",
|
|
49
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
# found in the LICENSE file.
|
|
5
|
+
|
|
6
|
+
"""iOS Unit Test Generator MCP Server.
|
|
7
|
+
|
|
8
|
+
This package provides MCP tools to generate unit tests for iOS Edge/Chromium code.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "4.19.3"
|
|
12
|
+
|
|
13
|
+
from .server import mcp, main
|
|
14
|
+
|
|
15
|
+
__all__ = ["mcp", "main", "__version__"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
# found in the LICENSE file.
|
|
5
|
+
|
|
6
|
+
"""Entry point for running the MCP server directly with `python -m ios_unittest_generator`."""
|
|
7
|
+
|
|
8
|
+
from .server import main
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
main()
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
# found in the LICENSE file.
|
|
5
|
+
|
|
6
|
+
"""BUILD file manipulation utilities.
|
|
7
|
+
|
|
8
|
+
This module provides functions for updating BUILD.gn and BUILD_edge.gni files.
|
|
9
|
+
Refactored from the monolithic _add_component_to_test_target function.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
from typing import List, Optional, Tuple
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BuildFileUpdater:
|
|
18
|
+
"""Handles BUILD file updates with strategy pattern."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, build_content: str, component_target: str, target_name: str):
|
|
21
|
+
"""Initialize the updater.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
build_content: Content of the BUILD file
|
|
25
|
+
component_target: Target path like "//components/xxx/ios:unit_tests"
|
|
26
|
+
target_name: Test target name ('ios_chrome_unittests', etc.)
|
|
27
|
+
"""
|
|
28
|
+
self.build_content = build_content
|
|
29
|
+
self.component_target = component_target
|
|
30
|
+
self.target_name = target_name
|
|
31
|
+
|
|
32
|
+
def update(self) -> Tuple[bool, str]:
|
|
33
|
+
"""Update BUILD file using appropriate strategy.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
(success, modified_content) tuple
|
|
37
|
+
"""
|
|
38
|
+
# Try strategies in order
|
|
39
|
+
strategies = [
|
|
40
|
+
self._strategy_edge_components_template,
|
|
41
|
+
self._strategy_edge_ios_template,
|
|
42
|
+
self._strategy_direct_target,
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
for strategy in strategies:
|
|
46
|
+
success, content = strategy()
|
|
47
|
+
if success:
|
|
48
|
+
return True, content
|
|
49
|
+
|
|
50
|
+
return False, self.build_content
|
|
51
|
+
|
|
52
|
+
def _strategy_edge_components_template(self) -> Tuple[bool, str]:
|
|
53
|
+
"""Strategy 0: edge_overlay_test_components_unittests template."""
|
|
54
|
+
if self.target_name != 'components_unittests':
|
|
55
|
+
return False, self.build_content
|
|
56
|
+
|
|
57
|
+
if 'edge_overlay_test_components_unittests' not in self.build_content:
|
|
58
|
+
return False, self.build_content
|
|
59
|
+
|
|
60
|
+
sys.stderr.write(f"\n[STRATEGY 0] Edge components template\n")
|
|
61
|
+
sys.stderr.flush()
|
|
62
|
+
|
|
63
|
+
# Find template definition
|
|
64
|
+
template_match = self._find_template_definition('edge_overlay_test_components_unittests')
|
|
65
|
+
if not template_match:
|
|
66
|
+
sys.stderr.write(f"[FAIL] Template not found\n")
|
|
67
|
+
sys.stderr.flush()
|
|
68
|
+
return False, self.build_content
|
|
69
|
+
|
|
70
|
+
template_content = template_match.group(1)
|
|
71
|
+
template_start = template_match.start(1)
|
|
72
|
+
|
|
73
|
+
# Find if (is_ios || is_android) block
|
|
74
|
+
ios_block = self._find_ios_conditional_block(template_content)
|
|
75
|
+
if not ios_block:
|
|
76
|
+
sys.stderr.write(f"[FAIL] iOS conditional block not found\n")
|
|
77
|
+
sys.stderr.flush()
|
|
78
|
+
return False, self.build_content
|
|
79
|
+
|
|
80
|
+
ios_content, ios_start, ios_end = ios_block
|
|
81
|
+
|
|
82
|
+
# Find or create deps += array
|
|
83
|
+
deps_match = re.search(r'(deps\s*\+=\s*\[)(.*?)(\])', ios_content, re.DOTALL)
|
|
84
|
+
|
|
85
|
+
if deps_match:
|
|
86
|
+
# Insert into existing deps
|
|
87
|
+
return self._insert_into_deps_array(
|
|
88
|
+
template_start + ios_start + deps_match.start(2),
|
|
89
|
+
template_start + ios_start + deps_match.end(2),
|
|
90
|
+
deps_match.group(2),
|
|
91
|
+
indent=' '
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
# Create new deps += array
|
|
95
|
+
insert_pos = template_start + ios_start
|
|
96
|
+
new_deps = f'\n deps += [\n "{self.component_target}",\n ]'
|
|
97
|
+
new_content = self.build_content[:insert_pos] + new_deps + self.build_content[insert_pos:]
|
|
98
|
+
sys.stderr.write(f"[OK] Created new deps += section\n")
|
|
99
|
+
sys.stderr.flush()
|
|
100
|
+
return True, new_content
|
|
101
|
+
|
|
102
|
+
def _strategy_edge_ios_template(self) -> Tuple[bool, str]:
|
|
103
|
+
"""Strategy 1: edge_overlay_test_ios_chrome_unittests template."""
|
|
104
|
+
if self.target_name != 'ios_chrome_unittests':
|
|
105
|
+
return False, self.build_content
|
|
106
|
+
|
|
107
|
+
if 'edge_overlay_test_ios_chrome_unittests' not in self.build_content:
|
|
108
|
+
return False, self.build_content
|
|
109
|
+
|
|
110
|
+
sys.stderr.write(f"\n[STRATEGY 1] Edge iOS template\n")
|
|
111
|
+
sys.stderr.flush()
|
|
112
|
+
|
|
113
|
+
pattern = r'(edge_overlay_test_ios_chrome_unittests\s*\([^)]+\)\s*\{[^}]*?deps\s*(?:\+)?=\s*\[)(.*?)(\])'
|
|
114
|
+
match = re.search(pattern, self.build_content, re.DOTALL)
|
|
115
|
+
|
|
116
|
+
if not match:
|
|
117
|
+
return False, self.build_content
|
|
118
|
+
|
|
119
|
+
return self._insert_into_deps_array(
|
|
120
|
+
match.start(2),
|
|
121
|
+
match.end(2),
|
|
122
|
+
match.group(2),
|
|
123
|
+
indent=' '
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def _strategy_direct_target(self) -> Tuple[bool, str]:
|
|
127
|
+
"""Strategy 2: Find target directly by name."""
|
|
128
|
+
sys.stderr.write(f"\n[STRATEGY 2] Direct target search\n")
|
|
129
|
+
sys.stderr.flush()
|
|
130
|
+
|
|
131
|
+
# Try to find test(...) or source_set(...)
|
|
132
|
+
pattern = rf'((?:source_set|test)\s*\(\s*"{re.escape(self.target_name)}"\s*\)\s*\{{.*?deps\s*(?:\+)?=\s*\[)(.*?)(\])'
|
|
133
|
+
match = re.search(pattern, self.build_content, re.DOTALL)
|
|
134
|
+
|
|
135
|
+
if not match:
|
|
136
|
+
sys.stderr.write(f"[FAIL] Target not found\n")
|
|
137
|
+
sys.stderr.flush()
|
|
138
|
+
return False, self.build_content
|
|
139
|
+
|
|
140
|
+
return self._insert_into_deps_array(
|
|
141
|
+
match.start(2),
|
|
142
|
+
match.end(2),
|
|
143
|
+
match.group(2),
|
|
144
|
+
indent=' '
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def _find_template_definition(self, template_name: str) -> Optional[re.Match]:
|
|
148
|
+
"""Find template definition in BUILD file."""
|
|
149
|
+
template_pattern = rf'template\s*\(\s*"{template_name}"\s*\)\s*\{{'
|
|
150
|
+
match = re.search(template_pattern, self.build_content)
|
|
151
|
+
|
|
152
|
+
if not match:
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
# Find matching closing brace
|
|
156
|
+
start_pos = match.end()
|
|
157
|
+
brace_count = 1
|
|
158
|
+
pos = start_pos
|
|
159
|
+
|
|
160
|
+
while pos < len(self.build_content) and brace_count > 0:
|
|
161
|
+
if self.build_content[pos] == '{':
|
|
162
|
+
brace_count += 1
|
|
163
|
+
elif self.build_content[pos] == '}':
|
|
164
|
+
brace_count -= 1
|
|
165
|
+
pos += 1
|
|
166
|
+
|
|
167
|
+
if brace_count == 0:
|
|
168
|
+
# Create a match object for the template content
|
|
169
|
+
class TemplateMatch:
|
|
170
|
+
def __init__(self, content, start, end):
|
|
171
|
+
self._content = content
|
|
172
|
+
self._start = start
|
|
173
|
+
self._end = end
|
|
174
|
+
|
|
175
|
+
def group(self, n):
|
|
176
|
+
if n == 1:
|
|
177
|
+
return self._content[self._start:self._end]
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
def start(self, n):
|
|
181
|
+
return self._start
|
|
182
|
+
|
|
183
|
+
def end(self, n):
|
|
184
|
+
return self._end
|
|
185
|
+
|
|
186
|
+
return TemplateMatch(self.build_content, start_pos, pos - 1)
|
|
187
|
+
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
def _find_ios_conditional_block(self, content: str) -> Optional[Tuple[str, int, int]]:
|
|
191
|
+
"""Find if (is_ios || is_android) or if (is_ios) block."""
|
|
192
|
+
# Try is_ios || is_android first
|
|
193
|
+
markers = [
|
|
194
|
+
'if (is_ios || is_android) {',
|
|
195
|
+
'if (is_ios) {',
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
for marker in markers:
|
|
199
|
+
pos = content.find(marker)
|
|
200
|
+
if pos != -1:
|
|
201
|
+
block_start = pos + len(marker)
|
|
202
|
+
brace_count = 1
|
|
203
|
+
close_pos = block_start
|
|
204
|
+
|
|
205
|
+
while close_pos < len(content) and brace_count > 0:
|
|
206
|
+
if content[close_pos] == '{':
|
|
207
|
+
brace_count += 1
|
|
208
|
+
elif content[close_pos] == '}':
|
|
209
|
+
brace_count -= 1
|
|
210
|
+
close_pos += 1
|
|
211
|
+
|
|
212
|
+
if brace_count == 0:
|
|
213
|
+
block_content = content[block_start:close_pos - 1]
|
|
214
|
+
return (block_content, block_start, close_pos - 1)
|
|
215
|
+
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
def _insert_into_deps_array(
|
|
219
|
+
self,
|
|
220
|
+
deps_start: int,
|
|
221
|
+
deps_end: int,
|
|
222
|
+
deps_content: str,
|
|
223
|
+
indent: str
|
|
224
|
+
) -> Tuple[bool, str]:
|
|
225
|
+
"""Insert component target into deps array alphabetically.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
deps_start: Start position of deps content
|
|
229
|
+
deps_end: End position of deps content
|
|
230
|
+
deps_content: Current deps content
|
|
231
|
+
indent: Indentation to use for entries
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
(success, modified_content) tuple
|
|
235
|
+
"""
|
|
236
|
+
# Parse existing deps
|
|
237
|
+
deps_lines = deps_content.split('\n')
|
|
238
|
+
deps_entries = []
|
|
239
|
+
for line in deps_lines:
|
|
240
|
+
stripped = line.strip()
|
|
241
|
+
if stripped and (stripped.startswith('"//') or stripped.startswith('"-=')):
|
|
242
|
+
deps_entries.append(stripped)
|
|
243
|
+
|
|
244
|
+
# Check if already exists
|
|
245
|
+
new_entry = f'"{self.component_target}",'
|
|
246
|
+
if new_entry.strip() in ' '.join(deps_entries):
|
|
247
|
+
sys.stderr.write(f"[INFO] Component already in deps\n")
|
|
248
|
+
sys.stderr.flush()
|
|
249
|
+
return False, self.build_content
|
|
250
|
+
|
|
251
|
+
# Find insertion point (alphabetical)
|
|
252
|
+
insertion_idx = len(deps_entries)
|
|
253
|
+
for i, entry in enumerate(deps_entries):
|
|
254
|
+
if '-=' in entry:
|
|
255
|
+
continue
|
|
256
|
+
if entry > new_entry:
|
|
257
|
+
insertion_idx = i
|
|
258
|
+
break
|
|
259
|
+
insertion_idx = i + 1
|
|
260
|
+
|
|
261
|
+
# Insert new entry
|
|
262
|
+
deps_entries.insert(insertion_idx, new_entry)
|
|
263
|
+
|
|
264
|
+
# Reconstruct deps
|
|
265
|
+
new_deps_lines = [f'{indent}{entry}' for entry in deps_entries]
|
|
266
|
+
new_deps_content = '\n' + '\n'.join(new_deps_lines) + f'\n{indent[:-2]}'
|
|
267
|
+
|
|
268
|
+
# Replace in content
|
|
269
|
+
new_content = self.build_content[:deps_start] + new_deps_content + self.build_content[deps_end:]
|
|
270
|
+
sys.stderr.write(f"[OK] Component added to deps\n")
|
|
271
|
+
sys.stderr.flush()
|
|
272
|
+
return True, new_content
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def add_component_to_test_target(
|
|
276
|
+
build_content: str,
|
|
277
|
+
component_target: str,
|
|
278
|
+
target_name: str
|
|
279
|
+
) -> Tuple[bool, str]:
|
|
280
|
+
"""Add component target to the appropriate test target's deps.
|
|
281
|
+
|
|
282
|
+
Refactored version using strategy pattern.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
build_content: Content of BUILD file
|
|
286
|
+
component_target: Target path like "//components/xxx/ios:unit_tests"
|
|
287
|
+
target_name: Test target name ('ios_chrome_unittests', etc.)
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
(success, modified_content) tuple
|
|
291
|
+
"""
|
|
292
|
+
updater = BuildFileUpdater(build_content, component_target, target_name)
|
|
293
|
+
return updater.update()
|