Files
FunASR/tests/test_docs_funasr_install_commands.py
T

486 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import re
import shlex
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
DOCS_WITH_CURRENT_FUNASR_INSTALL = [
"examples/industrial_data_pretraining/fun_asr_nano/docs/finetune.md",
"examples/industrial_data_pretraining/fun_asr_nano/docs/finetune_zh.md",
]
PUBLIC_DOCS_SHOULD_USE_CURRENT_HOSTS = [
"benchmarks/benchmark_pipeline_cer.md",
"docs/installation/installation.md",
"docs/installation/installation_zh.md",
"model_zoo/modelscope_models.md",
"model_zoo/readme.md",
"runtime/docs/SDK_advanced_guide_offline.md",
"runtime/docs/SDK_advanced_guide_offline_en.md",
"runtime/docs/SDK_advanced_guide_offline_en_zh.md",
"runtime/docs/SDK_advanced_guide_offline_gpu.md",
"runtime/docs/SDK_advanced_guide_offline_gpu_zh.md",
"runtime/docs/SDK_advanced_guide_offline_zh.md",
"runtime/docs/SDK_advanced_guide_online.md",
"runtime/docs/SDK_advanced_guide_online_zh.md",
"runtime/python/grpc/Readme.md",
"runtime/python/libtorch/README.md",
"runtime/python/onnxruntime/README.md",
"runtime/python/websocket/README.md",
"docs/m2met2/Baseline.md",
"examples/industrial_data_pretraining/monotonic_aligner/README_zh.md",
"examples/industrial_data_pretraining/sense_voice/README.md",
"examples/industrial_data_pretraining/sense_voice/README_zh.md",
"examples/industrial_data_pretraining/sense_voice/README_ja.md",
"runtime/html5/readme.md",
"runtime/html5/readme_zh.md",
]
PUBLIC_ENTRYPOINTS_SHOULD_USE_CURRENT_REPO_URLS = [
"model_zoo/modelscope_models.md",
"model_zoo/modelscope_models_zh.md",
"model_zoo/readme.md",
"web-pages/src/views/home/index.vue",
"web-pages/src/views/home/lxwjzxfw.vue",
"web-pages/src/views/home/sstx.vue",
"runtime/deploy_tools/funasr-runtime-deploy-offline-cpu-en.sh",
"runtime/deploy_tools/funasr-runtime-deploy-offline-cpu-zh.sh",
"runtime/deploy_tools/funasr-runtime-deploy-online-cpu-zh.sh",
"runtime/docs/SDK_tutorial.md",
"runtime/docs/SDK_tutorial_en.md",
"runtime/docs/SDK_tutorial_en_zh.md",
"runtime/docs/SDK_tutorial_online.md",
"runtime/docs/SDK_tutorial_online_zh.md",
"runtime/docs/SDK_tutorial_zh.md",
"runtime/python/http/server.py",
"runtime/python/libtorch/setup.py",
"runtime/python/onnxruntime/setup.py",
]
def test_current_funasr_install_commands_are_quoted():
for relpath in DOCS_WITH_CURRENT_FUNASR_INSTALL:
text = (ROOT / relpath).read_text()
assert '"funasr>=1.3.26"' in text
assert "funasr>=1.3.0" not in text
assert not re.search(r"pip install funasr>=", text)
@pytest.mark.parametrize("relpath", ["docs/vllm_guide.md", "docs/vllm_guide_zh.md"])
def test_vllm_service_install_uses_a_pinned_checkout(relpath):
text = (ROOT / relpath).read_text()
blocks = re.findall(r"^```bash\n(.*?)^```", text, re.M | re.S)
install = blocks[0]
assert 'python -m pip install "vllm==0.19.1"' in install
assert "git clone https://github.com/modelscope/FunASR.git FunASR-vllm" in install
assert re.search(r"^git checkout --detach [0-9a-f]{40}$", install, re.M)
assert "python -m pip install -e ." in install
assert "python -m pip check" in install
assert not re.search(r"pip install.*funasr[>=]", install)
def test_vllm_install_translations_have_identical_commands():
recipes = []
for relpath in ("docs/vllm_guide.md", "docs/vllm_guide_zh.md"):
text = (ROOT / relpath).read_text()
install = re.findall(r"^```bash\n(.*?)^```", text, re.M | re.S)[0]
recipes.append([
tokens for line in install.splitlines()
if (tokens := shlex.split(line, comments=True))
])
assert recipes[0] == recipes[1]
def test_fun_asr_nano_finetune_zh_uses_canonical_filename():
docs_dir = ROOT / "examples/industrial_data_pretraining/fun_asr_nano/docs"
assert (docs_dir / "finetune_zh.md").exists()
assert "fintune_zh.md" not in (docs_dir / "finetune.md").read_text()
def test_public_docs_use_current_repository_and_docs_hosts():
for relpath in PUBLIC_DOCS_SHOULD_USE_CURRENT_HOSTS:
text = (ROOT / relpath).read_text()
assert "github.com/alibaba/FunASR" not in text
assert "alibaba-damo-academy.github.io/FunASR" not in text
def test_public_entrypoints_use_current_repository_urls():
forbidden = [
"github.com/alibaba-damo-academy/FunASR",
"raw.githubusercontent.com/alibaba-damo-academy/FunASR",
]
for relpath in PUBLIC_ENTRYPOINTS_SHOULD_USE_CURRENT_REPO_URLS:
text = (ROOT / relpath).read_text()
for marker in forbidden:
assert marker not in text
def test_troubleshooting_faq_is_linked_from_readmes():
readme_links = [
("README.md", "./docs/troubleshooting.md"),
("README_zh.md", "./docs/troubleshooting_zh.md"),
]
for readme, link in readme_links:
text = (ROOT / readme).read_text()
assert link in text
def test_troubleshooting_faq_covers_common_install_and_deploy_failures():
docs = [
(ROOT / "docs/troubleshooting.md").read_text(),
(ROOT / "docs/troubleshooting_zh.md").read_text(),
]
required_markers = [
"torch",
"torchaudio",
"ModelScope",
"Hugging Face",
"funasr-server",
"/v1/audio/transcriptions",
"WebSocket",
"llama.cpp",
"GGUF",
"Deployment Help",
]
for text in docs:
for marker in required_markers:
assert marker in text
def test_tutorials_keep_model_license_boundaries_model_card_specific():
docs = {
"en": (ROOT / "docs/tutorial/README.md").read_text(),
"zh": (ROOT / "docs/tutorial/README_zh.md").read_text(),
}
assert "Each model weight has its own license" in docs["en"]
assert "model card explicitly links" in docs["en"]
assert "每个模型权重都有各自的许可" in docs["zh"]
assert "模型卡明确链接" in docs["zh"]
for text in docs.values():
assert "自由使用、复制、修改和分享FunASR模型" not in text
assert "free to use, copy, modify, and share FunASR models" not in text
def test_public_docs_do_not_advertise_stale_release_or_star_copy():
checked_docs = [
"docs/repository_roles.md",
"docs/repository_roles_zh.md",
"docs/blog_whisper_vs_funasr_zh.md",
"runtime/llama.cpp/README.md",
]
forbidden = [
"GitHub tags go up to `v1.3.13`",
"PyPI has published `1.3.14`",
"GitHub tag 至 `v1.3.13`",
"PyPI 已发布 `1.3.14`",
"(16K+ stars)",
"runtime-llamacpp-v0.1.7",
]
for relpath in checked_docs:
text = (ROOT / relpath).read_text()
for marker in forbidden:
assert marker not in text
def test_readme_model_tables_use_current_modelscope_entries():
readmes = [
(ROOT / "README.md").read_text(),
(ROOT / "README_zh.md").read_text(),
(ROOT / "README_ja.md").read_text(),
(ROOT / "README_ko.md").read_text(),
]
current_entries = [
"models/iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
"models/iic/punc_ct-transformer_cn-en-common-vocab471067-large/summary",
"models/iic/speech_fsmn_vad_zh-cn-16k-common-pytorch/summary",
]
stale_entries = [
"models/damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
"models/damo/punc_ct-transformer_cn-en-common-vocab471067-large/summary",
"models/damo/speech_fsmn_vad_zh-cn-16k-common-pytorch/summary",
]
for text in readmes:
assert current_entries[0] in text
assert stale_entries[0] not in text
for text in readmes[:2]:
for marker in current_entries:
assert marker in text
for marker in stale_entries:
assert marker not in text
def test_model_zoo_tables_use_current_paraformer_modelscope_entry():
docs = [
(ROOT / "model_zoo/modelscope_models.md").read_text(),
(ROOT / "model_zoo/modelscope_models_zh.md").read_text(),
(ROOT / "model_zoo/readme.md").read_text(),
]
current = (
"models/iic/"
"speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch"
"/summary"
)
stale = (
"models/damo/"
"speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch"
"/summary"
)
for text in docs:
assert current in text
assert stale not in text
def test_full_modelscope_tables_use_current_core_entries():
docs = [
(ROOT / "model_zoo/modelscope_models.md").read_text(),
(ROOT / "model_zoo/modelscope_models_zh.md").read_text(),
]
current_entries = [
"models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
"models/iic/punc_ct-transformer_cn-en-common-vocab471067-large/summary",
"models/iic/speech_fsmn_vad_zh-cn-16k-common-pytorch/summary",
]
stale_entries = [
"models/damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
"models/damo/punc_ct-transformer_cn-en-common-vocab471067-large/summary",
"models/damo/speech_fsmn_vad_zh-cn-16k-common-pytorch/summary",
]
for text in docs:
for marker in current_entries:
assert marker in text
for marker in stale_entries:
assert marker not in text
def test_runtime_benchmark_docs_use_current_core_modelscope_entries():
docs = [
(ROOT / "benchmarks/benchmark_pipeline_cer.md").read_text(),
(ROOT / "runtime/docs/benchmark_libtorch.md").read_text(),
(ROOT / "runtime/docs/benchmark_onnx.md").read_text(),
(ROOT / "runtime/docs/benchmark_onnx_cpp.md").read_text(),
]
current_entries = [
"models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
"models/iic/speech_fsmn_vad_zh-cn-16k-common-onnx/summary",
"models/iic/punc_ct-transformer_zh-cn-common-vocab272727-onnx/summary",
]
stale_entries = [
entry.replace("models/iic/", "models/damo/") for entry in current_entries
]
combined = "\n".join(docs)
for marker in current_entries:
assert marker in combined
for marker in stale_entries:
assert marker not in combined
def test_model_zoo_landing_tables_link_huggingface_repos():
docs = [
(
(ROOT / "model_zoo/readme.md").read_text(),
[
"https://huggingface.co/funasr/paraformer-zh",
"https://huggingface.co/funasr/paraformer-zh-streaming",
"https://huggingface.co/funasr/ct-punc",
"https://huggingface.co/funasr/fsmn-vad",
],
),
(
(ROOT / "model_zoo/readme_zh.md").read_text(),
[
"https://huggingface.co/funasr/paraformer-zh",
"https://huggingface.co/funasr/paraformer-zh-streaming",
],
),
]
for text, required_hf_links in docs:
for link in required_hf_links:
assert link in text
def test_model_zoo_landing_tables_use_current_core_modelscope_entries():
docs = [
(
(ROOT / "model_zoo/readme.md").read_text(),
[
"models/iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
"models/iic/punc_ct-transformer_cn-en-common-vocab471067-large/summary",
"models/iic/speech_fsmn_vad_zh-cn-16k-common-pytorch/summary",
],
[
"models/damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
"models/damo/punc_ct-transformer_cn-en-common-vocab471067-large/summary",
"models/damo/speech_fsmn_vad_zh-cn-16k-common-pytorch/summary",
],
),
(
(ROOT / "model_zoo/readme_zh.md").read_text(),
[
"models/iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
],
[
"models/damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch/summary",
"models/damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online/summary",
],
),
]
for text, current_entries, stale_entries in docs:
for marker in current_entries:
assert marker in text
for marker in stale_entries:
assert marker not in text
def test_readme_model_tables_surface_public_gguf_entries():
readmes = [
(ROOT / "README.md").read_text(),
(ROOT / "README_zh.md").read_text(),
(ROOT / "README_ja.md").read_text(),
(ROOT / "README_ko.md").read_text(),
]
for text in readmes:
assert "https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF" in text
assert "https://huggingface.co/FunAudioLLM/SenseVoiceSmall-GGUF" in text
assert "https://huggingface.co/FunAudioLLM/Fun-ASR-MLT-Nano-GGUF" not in text
def test_top_level_readmes_surface_current_release_and_edge_runtime():
readmes = {
name: (ROOT / name).read_text()
for name in ("README.md", "README_zh.md", "README_ja.md", "README_ko.md")
}
for name, text in readmes.items():
assert 'python -m pip install -U "funasr==1.4.14"' in text, name
assert "https://github.com/modelscope/FunASR/releases/tag/v1.4.14" in text, name
assert "runtime-llamacpp-v0.2.6" in text, name
assert "https://www.funasr.com/en/deploy/llama-cpp.html" in readmes["README.md"]
assert "https://www.funasr.com/deploy/llama-cpp.html" in readmes["README_zh.md"]
for name in ("README_ja.md", "README_ko.md"):
assert "https://www.funasr.com/en/deploy/llama-cpp.html" in readmes[name]
for name in ("README.md", "README_zh.md"):
text = readmes[name]
for asset in (
"funasr-llamacpp-linux-x64-vulkan.tar.gz",
"funasr-llamacpp-windows-x64-vulkan.zip",
"funasr-llamacpp-windows-x64-cuda.zip",
"funasr-llamacpp-windows-x64-cuda-blackwell.zip",
):
assert (
f"releases/download/runtime-llamacpp-v0.2.6/{asset}" in text
), name
assert "releases/download/runtime-llamacpp-v0.2.1/" not in text, name
def test_top_level_readme_news_stays_concise():
headings = {
"README.md": "## What's new",
"README_zh.md": "## 最新动态",
"README_ja.md": "## 最新情報",
"README_ko.md": "## 최신 소식",
}
for name, heading in headings.items():
text = (ROOT / name).read_text()
news = text.split(heading, 1)[1].split("\n---", 1)[0]
assert news.count("\n- ") <= 5, name
assert "https://github.com/modelscope/FunASR/releases" in news, name
def test_repository_roadmap_tracks_current_delivery_and_open_work():
docs = [
(ROOT / "docs/repository_roles.md").read_text(),
(ROOT / "docs/repository_roles_zh.md").read_text(),
]
for text in docs:
assert "1.4.14" in text
assert "v1.3.26" not in text
assert "runtime-llamacpp-v0.2.6" in text
assert "MOSS-Transcribe-Diarize" in text
assert "https://github.com/modelscope/FunASR/issues/3496" in text
assert "https://github.com/modelscope/FunASR/issues/3528" in text
assert "https://github.com/modelscope/FunASR/issues/3479" in text
assert "https://github.com/huggingface/transformers/pull/46180" in text
assert "speaker identities" not in docs[0]
assert "说话人身份识别" not in docs[1]
def test_repository_roadmap_exposes_live_contribution_entry_points():
docs = [
(ROOT / "docs/repository_roles.md").read_text(),
(ROOT / "docs/repository_roles_zh.md").read_text(),
]
live_queries = [
"is%3Aissue+is%3Aopen+label%3A%22help+wanted%22",
"is%3Aissue+is%3Aopen+label%3A%22ready+for+PR%22",
]
for text in docs:
for query in live_queries:
assert query in text
assert "needs feedback" in text
assert "exact commit" in docs[0]
assert "acceptance evidence" in docs[0]
assert "exact commit" in docs[1]
assert "验收证据" in docs[1]
contributing = (ROOT / "CONTRIBUTING.md").read_text()
assert "## Find a task" in contributing
for query in live_queries:
assert query in contributing
assert "needs feedback" in contributing
def test_realtime_demo_documents_partial_and_hotword_boundaries():
text = (
ROOT
/ "examples/industrial_data_pretraining/fun_asr_nano/docs/realtime_demo.md"
).read_text()
required = [
"data.sentences.map",
"data.partial || \"\"",
"partial_start_ms",
"--partial-window-sec",
"不是确定性文本替换",
"HOTWORDS:Tool,客製化,季會",
"后处理",
]
for marker in required:
assert marker in text