zapretgui/tests/test_preset_text_outline.py

160 lines
6.5 KiB
Python

from __future__ import annotations
import os
import sys
import unittest
from pathlib import Path
PROJECT_SRC = Path(__file__).resolve().parents[1] / "src"
if str(PROJECT_SRC) not in sys.path:
sys.path.insert(0, str(PROJECT_SRC))
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
BUILTIN = PROJECT_SRC / "presets" / "builtin"
SAMPLE = """# Preset: Пример
# BuiltinVersion: 1.0
--lua-init=@lua/zapret-lib.lua
--lua-init=@lua/zapret-antidpi.lua
--ctrack-disable=0
--wf-tcp-out=80,443
--wf-udp-out=443
--blob=tls_google:@bin/tls_clienthello_www_google_com.bin
--name=youtube
--filter-tcp=443
--hostlist=lists/youtube.txt
--out-range=-d8
--payload=tls_client_hello
--lua-desync=multisplit:pos=1
--new
--name=discord
--filter-udp=50000-50100
--ipset=lists/ipset-discord.txt
--lua-desync=fake:blob=tls_google
"""
def _lines(text: str, indexes) -> list[str]:
source = text.split("\n")
return [source[index] for index in indexes]
class PresetTextOutlineTests(unittest.TestCase):
def test_every_part_is_found_in_the_right_lines(self) -> None:
from profile.parser import preset_text_outline
outline = preset_text_outline(SAMPLE, engine="winws2")
self.assertEqual(_lines(SAMPLE, outline.header), ["# Preset: Пример", "# BuiltinVersion: 1.0"])
self.assertEqual(len(outline.lua_init), 2)
self.assertEqual(_lines(SAMPLE, outline.engine_options), ["--ctrack-disable=0"])
self.assertEqual(_lines(SAMPLE, outline.interception), ["--wf-tcp-out=80,443", "--wf-udp-out=443"])
self.assertEqual(len(outline.blobs), 1)
self.assertEqual(len(outline.profiles), 2)
first, second = outline.profiles
self.assertIsNone(first.new_line)
self.assertEqual(_lines(SAMPLE, first.name), ["--name=youtube"])
self.assertEqual(_lines(SAMPLE, first.match), ["--filter-tcp=443", "--hostlist=lists/youtube.txt"])
self.assertEqual(_lines(SAMPLE, first.packets), ["--out-range=-d8", "--payload=tls_client_hello"])
self.assertEqual(_lines(SAMPLE, first.strategy), ["--lua-desync=multisplit:pos=1"])
self.assertEqual(_lines(SAMPLE, [second.new_line]), ["--new"])
self.assertEqual(second.packets, ())
def test_outline_follows_the_text_not_remembered_line_numbers(self) -> None:
from ui.onboarding.preset_sections import build_outline, section_lines, section_text_values
edited = SAMPLE.replace(
"--blob=tls_google:@bin/tls_clienthello_www_google_com.bin\n",
"--blob=tls_vk:@bin/tls_clienthello_vk_com.bin\n--blob=quic1:@bin/quic_1.bin\n",
).replace("--lua-desync=multisplit:pos=1", "--lua-desync=fakedsplit:pos=2")
before = build_outline(SAMPLE, zapret2=True)
after = build_outline(edited, zapret2=True)
self.assertEqual(section_text_values(before, "blobs"), {"count": "1", "example": "tls_google"})
self.assertEqual(section_text_values(after, "blobs"), {"count": "2", "example": "tls_vk"})
self.assertEqual(section_text_values(after, "profile_strategy")["technique"], "fakedsplit")
self.assertEqual(
[line + 1 for line in section_lines(after, "profile_strategy")],
[line + 2 for line in section_lines(before, "profile_strategy")],
)
def test_missing_part_has_no_lines(self) -> None:
from ui.onboarding.preset_sections import build_outline, section_lines
text = "--wf-tcp-out=443\n\n--filter-tcp=443\n--lua-desync=fake\n"
outline = build_outline(text, zapret2=True)
for section in ("header", "lua_init", "blobs", "engine_options", "profile_name", "profile_packets", "profile_new"):
self.assertEqual(section_lines(outline, section), (), section)
self.assertEqual(len(section_lines(outline, "interception")), 1)
def test_outline_agrees_with_parser_on_every_builtin_preset(self) -> None:
from profile.parser import parse_preset_text, preset_text_outline
checked = 0
for engine in ("winws2", "winws1"):
for path in sorted((BUILTIN / engine).glob("*.txt")):
text = path.read_text(encoding="utf-8")
preset = parse_preset_text(text, engine=engine)
outline = preset_text_outline(text, engine=engine)
with self.subTest(preset=path.name):
self.assertEqual(len(outline.profiles), len(preset.profiles))
self.assertEqual(
len(outline.header),
len([line for line in preset.header_lines if line.strip()]),
)
for profile_outline, profile in zip(outline.profiles, preset.profiles):
strategy_like = profile_outline.packets + profile_outline.strategy
kinds = [s for s in profile.segments if s.kind in {"strategy", "strategy_filter"}]
self.assertEqual(len(strategy_like), len(kinds))
checked += 1
self.assertGreater(checked, 100)
class PresetSectionEditorTests(unittest.TestCase):
def setUp(self) -> None:
from PyQt6.QtWidgets import QApplication
QApplication.instance() or QApplication([])
def test_highlight_and_scroll_do_not_touch_the_text(self) -> None:
from PyQt6.QtWidgets import QPlainTextEdit
from ui.onboarding.preset_sections import (
build_outline,
editor_lines_rect,
scroll_editor_to_line,
section_lines,
)
long_text = SAMPLE + "\n".join(f"--new\n--filter-tcp={port}\n--lua-desync=fake" for port in range(1000, 1060))
editor = QPlainTextEdit()
editor.resize(600, 200)
editor.setPlainText(long_text)
editor.show()
try:
outline = build_outline(editor.toPlainText(), zapret2=True)
lines = section_lines(outline, "profile_strategy")
revision = editor.document().revision()
cursor_position = editor.textCursor().position()
scroll_editor_to_line(editor, min(lines))
target = editor_lines_rect(editor, lines)
self.assertIsNotNone(target)
widget, rect = target
self.assertIs(widget, editor)
self.assertTrue(editor.rect().contains(rect))
self.assertEqual(editor.document().revision(), revision)
self.assertEqual(editor.textCursor().position(), cursor_position)
self.assertEqual(editor.toPlainText(), long_text)
finally:
editor.close()
editor.deleteLater()
if __name__ == "__main__":
unittest.main()