78 lines
9.2 KiB
Python
78 lines
9.2 KiB
Python
"""Проверки общей консоли на настоящем Qt без физического устройства.""" # Проверяем сохранность истории, фильтры и очистку при асинхронном обновлении.
|
||
import os # Виртуальная платформа выбирается до импорта Qt.
|
||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") # Тесты не открывают окна на рабочем столе.
|
||
import pytest # Фикстура даёт каждому сценарию независимую консоль.
|
||
from set_devices.qt_ports.console import ConsoleView, QApplication # Используем реальный порт templates, а не имитацию обработчиков.
|
||
|
||
|
||
@pytest.fixture # Создаём отдельный журнал для проверки независимости состояний.
|
||
def console(): # Ссылка на приложение удерживается до завершения сценария.
|
||
app = QApplication.instance() or QApplication([]) # Qt требует приложение перед созданием виджетов.
|
||
view = ConsoleView(limit=3, payloads=True) # Малый предел проверяет вытеснение без большого массива данных.
|
||
yield view # Сценарий управляет настоящими сигналами флажков и кнопок.
|
||
view.panel.deleteLater() # Удаляется контейнер вместе с редактором и таймером.
|
||
app.processEvents() # Отложенное удаление доставляется до следующего сценария.
|
||
|
||
|
||
def test_pause_keeps_bounded_history_and_clear_cannot_resurrect(console): # Очистка на паузе должна удалять также невидимые данные.
|
||
console.appendPlainText("первая") # Первая запись сразу видна на экране.
|
||
console.pause.setChecked(True) # Замораживаем только представление.
|
||
for index in range(5): console.appendPlainText("строка %d" % index) # На паузе поступает больше данных, чем помещается в истории.
|
||
assert console.toPlainText() == "первая" and len(console.records) == 3 # Экран заморожен, память ограничена.
|
||
console.pause.setChecked(False) # Возобновление показывает последние сохранённые строки.
|
||
assert console.toPlainText().splitlines() == ["строка 2", "строка 3", "строка 4"] # Вытеснённые записи больше не появляются.
|
||
console.appendPlainText("ожидает таймер") # Полный буфер ставит пакетное обновление в очередь.
|
||
assert console.timer.isActive() # Проверяем именно очистку при ожидающем обновлении.
|
||
console.pause.setChecked(True) # Очистка должна работать и при замороженном экране.
|
||
console.clear_button.click() # Пользователь удаляет весь журнал через общую панель.
|
||
console.pause.setChecked(False) # Возобновление не должно вернуть старые данные.
|
||
assert not console.records and not console.toPlainText() and not console.timer.isActive() # Документ, история и очередь согласованы.
|
||
|
||
|
||
def test_filters_restore_hidden_rows_and_preserve_service_errors(console): # Фильтры направлений не должны скрывать ошибки без метки TX/RX.
|
||
console.appendPlainText("[12:00:00] TX команда") # Начальная метка после времени определяет направление.
|
||
console.appendPlainText("12:00:01.123 RX ответ") # Поддерживаем существующий формат временных меток SETGUI.
|
||
console.appendPlainText("#ERR RX timeout") # Слово RX внутри ошибки не делает её принятым пакетом.
|
||
console.show_rx.setChecked(False) # Скрываем только входящие пакеты.
|
||
console.show_tx.setChecked(False) # Оба направления можно выключить одновременно.
|
||
assert console.toPlainText() == "#ERR RX timeout" # Служебная ошибка остаётся доступной оператору.
|
||
console.search.setText("ОТВЕТ") # Регистр запроса не должен влиять на поиск.
|
||
console.show_rx.setChecked(True) # Входящие данные восстанавливаются из сохранённой истории.
|
||
assert console.toPlainText() == "12:00:01.123 RX ответ" # Поиск применяется к той же записи без повторного приёма.
|
||
console.search.clear() # Снимаем текстовый фильтр.
|
||
assert len(console.records) == 3 # Фильтры не удаляют события.
|
||
|
||
|
||
def test_payload_and_utf8_export_use_visible_snapshot(console, tmp_path): # Сохранение и копирование соответствуют выбранному представлению.
|
||
console.append_message("RX пакет <текст>", direction="RX", payload=b"A\x00\xff") # Payload содержит печатный, управляющий и не-ASCII байты.
|
||
console.display.setCurrentText("HEX + ASCII") # Выбираем совместное представление байтов.
|
||
assert console.toPlainText() == "RX пакет <текст> · 41 00 FF | A.." # Непечатные байты заменяются точками без потери HEX.
|
||
path = tmp_path / "журнал.txt" # Проверяем кириллицу в имени и содержимом файла.
|
||
console.save_to(path) # Экспорт выполняется без диалога, сохраняя тот же код записи.
|
||
assert path.read_text(encoding="utf-8") == console.toPlainText() # UTF-8 сохраняет фактически видимый текст.
|
||
console.copy_visible() # Кнопка копирует весь показанный журнал.
|
||
assert QApplication.clipboard().text() == console.toPlainText() # Буфер обмена совпадает с экспортом.
|
||
|
||
|
||
def test_multiline_html_and_snapshot_obey_limit(console): # Предел относится к строкам, а не к числу вызовов записи.
|
||
console.appendPlainText("0\n1\n2\n3\n4") # Один пакет расшифровки превышает лимит документа и истории.
|
||
console.refresh() # Применяем запланированное пакетное обновление.
|
||
assert console.toPlainText() == "2\n3\n4" # Старые строки не возвращаются при смене фильтра.
|
||
console.setPlainText("снимок") # Потребитель может передать результат повторного разбора кадров целиком.
|
||
console.appendHtml('<span style="color:#ff0000"><ошибка></span>') # Проверяем совместимость прежнего цветного API SETGUI.
|
||
console.search.setText("ошибка") # Поиск должен работать по тексту без тегов разметки.
|
||
assert console.toPlainText() == "<ошибка>" # Спецсимволы устройства остаются обычным текстом.
|
||
console.search.clear() # Возвращаем обе записи с исходным оформлением.
|
||
assert console.toPlainText() == "снимок\n<ошибка>" # Фильтр не теряет предыдущий снимок истории.
|
||
|
||
|
||
def test_clear_notifies_external_owner_once(console): # Владелец может очистить свой буфер и повторно вызвать clear без рекурсии.
|
||
external = ["старый кадр"] # Имитируем отдельную историю протокольных кадров приложения.
|
||
def clear_owner(): # Обработчик очищает внешний источник повторного форматирования.
|
||
external.clear() # Старые кадры не вернутся после смены парсера.
|
||
console.clear() # Повторный программный вызов не должен снова эмитировать пользовательский сигнал.
|
||
console.userCleared.connect(clear_owner) # Подключаем тот же контракт, что консоли платы и TX/RX SETGUI.
|
||
console.appendPlainText("строка") # До очистки журнал содержит данные.
|
||
console.clear_button.click() # Нажимаем настоящую кнопку Qt.
|
||
assert not external and not console.toPlainText() # Обе истории очищены одним действием.
|