Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions packages/solaris/solaris/analyze/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@ class DataSourceDirSettings(BaseSettings):

@model_validator(mode='after')
def combine_dirs(self) -> Self:
self.HTML5_DIR = self.HTML5_DIR
self.PATCH_DIR = self.PATCH_DIR
self.UNITY_DIR = self.UNITY_DIR
self.FLASH_DIR = self.FLASH_DIR
# 分类目录未显式指定(环境变量或构造参数)时,一律从当前 BASE_DIR 派生,
# 保证 BASE_DIR 来自 -w/--source-dir 或环境变量时子目录跟随变化
for field_name, sub_dir in (
('PATCH_DIR', 'patch'),
('HTML5_DIR', 'html5'),
('UNITY_DIR', 'unity'),
('FLASH_DIR', 'flash'),
):
if field_name not in self.model_fields_set:
setattr(self, field_name, self.BASE_DIR / sub_dir)
return self
13 changes: 10 additions & 3 deletions packages/solaris/solaris/cli/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,15 @@ def analyze(
"""分析赛尔号客户端数据并生成API数据或文档。"""

ctx.ensure_object(dict)
# 不同的包可能导出同一个分析器类(例如同时指定父包与其子包),
# 重复的类会让拓扑排序的入度统计失配,误报循环依赖
analyzer_classes: list[type[BaseAnalyzer]] = []
seen_analyzers: set[type[BaseAnalyzer]] = set()
for name in package_name:
analyzer_classes.extend(import_analyzer_classes(name))
for analyzer_class in import_analyzer_classes(name):
if analyzer_class not in seen_analyzers:
seen_analyzers.add(analyzer_class)
analyzer_classes.append(analyzer_class)

ctx.obj['analyzer_classes'] = analyzer_classes

Expand All @@ -91,9 +97,10 @@ def analyze(
ctx.exit()

# 设置数据源目录
settings = DataSourceDirSettings()
if source_dir:
settings.BASE_DIR = Path(source_dir)
settings = DataSourceDirSettings(BASE_DIR=source_dir)
else:
settings = DataSourceDirSettings()
DataImportConfig.set_source_dir(settings)
ctx.obj['data_source_settings'] = settings

Expand Down
7 changes: 6 additions & 1 deletion packages/solaris/solaris/cli/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,14 @@ def parse(
) -> None:
"""批量解析赛尔号 Unity 端二进制数据"""

# 同 analyze:重复的包会让同一个 parser 被导入并执行多次
parser_classes = []
seen_parsers: set[type[BaseParser]] = set()
for name in package_name:
parser_classes.extend(import_parser_classes(name))
for parser_class in import_parser_classes(name):
if parser_class not in seen_parsers:
seen_parsers.add(parser_class)
parser_classes.append(parser_class)

if parser_names:
parser_classes, not_found = filter_parser_classes(parser_classes, parser_names)
Expand Down
35 changes: 35 additions & 0 deletions packages/solaris/tests/analyze/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from click.testing import CliRunner

from solaris.cli.analyze import analyze


def _listed_analyzer_names(output: str) -> list[str]:
names = []
for line in output.splitlines():
if line.startswith('分析器: '):
names.append(line.removeprefix('分析器: ').split(' |')[0])
return names


def test_overlapping_packages_do_not_duplicate_analyzers():
"""pet 包会递归导入 skill 包也导出的分析器,重复类曾让拓扑排序误报循环依赖"""
runner = CliRunner()
result = runner.invoke(
analyze,
[
'--package-name',
'solaris.analyze.analyzers.pet',
'--package-name',
'solaris.analyze.analyzers.skill',
'--list-analyzers',
'data',
],
catch_exceptions=False,
)

assert result.exit_code == 0
analyzer_names = _listed_analyzer_names(result.output)
assert len(analyzer_names) == len(set(analyzer_names))
# 两个包共有的分析器只保留一份
assert analyzer_names.count('PetAdvanceAnalyzer') == 1
assert 'SkillAnalyzer' in analyzer_names
45 changes: 45 additions & 0 deletions packages/solaris/tests/analyze/test_data_source_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from pathlib import Path

import pytest

from solaris.analyze.settings import DataSourceDirSettings


@pytest.fixture(autouse=True)
def clear_solaris_data_env(monkeypatch):
"""隔离开发机上可能存在的 SOLARIS_DATA_* 环境变量"""
for name in (
'SOLARIS_DATA_BASE_DIR',
'SOLARIS_DATA_PATCH_DIR',
'SOLARIS_DATA_HTML5_DIR',
'SOLARIS_DATA_UNITY_DIR',
'SOLARIS_DATA_FLASH_DIR',
):
monkeypatch.delenv(name, raising=False)


def test_sub_dirs_derive_from_base_dir():
settings = DataSourceDirSettings(BASE_DIR='custom')

assert settings.PATCH_DIR == Path('custom') / 'patch'
assert settings.HTML5_DIR == Path('custom') / 'html5'
assert settings.UNITY_DIR == Path('custom') / 'unity'
assert settings.FLASH_DIR == Path('custom') / 'flash'


def test_default_base_dir_keeps_source_layout():
settings = DataSourceDirSettings()

assert settings.BASE_DIR == Path('./source')
assert settings.UNITY_DIR == Path('./source') / 'unity'
assert settings.PATCH_DIR == Path('./source') / 'patch'


def test_explicit_sub_dir_wins_over_base_dir():
settings = DataSourceDirSettings(
BASE_DIR='custom',
UNITY_DIR='/elsewhere/unity',
)

assert settings.UNITY_DIR == Path('/elsewhere/unity')
assert settings.PATCH_DIR == Path('custom') / 'patch'