File size: 5,061 Bytes
2695555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
"""opentars --diagnostico / --autoteste: o relatório que o usuário roda no
PC dele. Aqui o Ollama, a IA e a calculadora são simulados; o que se testa
é que o relatório não quebra em nenhuma situação e que o teste da
calculadora confere o VISOR (não só o que a IA diz)."""

import contextlib
import io
import os
import tempfile

os.environ["TARS_ARQUIVO_AUTOTESTE"] = os.path.join(tempfile.mkdtemp(), "relatorio.txt")

from _util import carregar_tars  # noqa: E402

tars = carregar_tars()
import tars_autoteste  # noqa: E402


def rodar(completo):
    saida = io.StringIO()
    with contextlib.redirect_stdout(saida):
        codigo = tars_autoteste.executar(tars, completo=completo)
    return codigo, saida.getvalue()


# 1. Nada funcionando (sem Ollama, sem tela): relata, não quebra
tars._SESSION.get = lambda *a, **k: (_ for _ in ()).throw(ConnectionError("recusado"))
tars.ollama_online = lambda: False
codigo, texto = rodar(completo=True)
assert codigo == 1 and "Ollama" in texto and "não está respondendo" in texto, texto
assert "sem Ollama ou sem modelo de conversa" in texto
print("OK: sem Ollama, o relatório aponta o problema (e não quebra)")


# 2. Ollama e modelos no ar; a IA "abre a calculadora e clica"
class Resp:
    status_code = 200

    def json(self):
        return {"version": "0.12.3"}


CATALOGO = {
    "qwen3:8b": {"tag": "qwen3:8b", "familia": "qwen3", "parametros_b": 8.2, "categoria": "geral",
                 "vram_estimado_gb": 5.5, "capacidades": {"tools", "thinking"}, "capacidades_conhecidas": True},
    "qwen2.5:0.5b": {"tag": "qwen2.5:0.5b", "familia": "qwen2", "parametros_b": 0.5, "categoria": "simples",
                     "vram_estimado_gb": 0.9, "capacidades": {"tools"}, "capacidades_conhecidas": True},
}
tars._SESSION.get = lambda *a, **k: Resp()
tars.catalogar_modelos = lambda forcar=False: CATALOGO
tars.listar_modelos_instalados = lambda forcar=False: set(CATALOGO)
tars.detectar_gpu = lambda forcar=False: {"vram_total_gb": 6.0, "vram_livre_gb": 5.0, "fabricante": "NVIDIA"}
tars.medir_classificador = lambda frases: {"n": 34, "acertos_local": 31, "acertos_total": 32, "ms_medio": 0.4,
                                           "por_categoria": {}, "erros_total": [("frase x", "busca", "geral")],
                                           "erros_local": []}
tars.SESSAO_GRAFICA = "x11"
tars._buscar_app_no_sistema = lambda nome: {"tipo": "desktop", "nome": "Calculadora", "comando": ["gnome-calculator"]}
tars._capturar_tela = lambda: __import__("PIL.Image", fromlist=["Image"]).new("RGB", (1920, 1080), (40, 90, 160))
tars.listar_janelas_x = lambda: [{"id": 1, "titulo": "Calculadora", "classe": "gnome-calculator", "pid": 9}]
tars._termos_para_janela = lambda nome: [nome]
tars._pids_protegidos = lambda: set()
fechados = []
tars.fechar_aplicativo = lambda nome: fechados.append(nome) or {"sucesso": True}
visor = {"textos": ["9"]}
tars.acess.indisponivel = lambda: None
tars.acess.janelas = lambda: [{"app": "gnome-calculator", "titulo": "Calculadora", "pid": 9, "ativa": True, "no": None}]
tars.acess.achar_janela = lambda **k: {"titulo": "Calculadora", "no": None}
tars.acess.listar = lambda janela, filtro=None: {"elementos": {}, "textos": visor["textos"], "total": 0}

conversa_antes = list(tars.conversa)
arquivo_antes = tars.ARQUIVO_SESSOES


def ia_que_acerta(texto):
    assert "calculadora" in texto.lower() and tars.ARQUIVO_SESSOES != arquivo_antes, "o teste não usa o histórico real"
    for nome in ("7", "+", "2", "="):
        tars.executar_ferramenta("click_element", {"name": nome, "window": "calculadora"})
    return "O resultado é 9."


tars._DISPATCH_FERRAMENTAS["click_element"] = lambda a: {"sucesso": True, "mensagem": "SUCCESS"}
tars.processar_mensagem = ia_que_acerta
codigo, texto = rodar(completo=True)
assert codigo == 0, texto
assert "passou (o visor mostra 9)" in texto and "click_element×4" in texto, texto
assert "32/34 (94%)" in texto and "0.12.3" in texto and "NVIDIA 6.0 GB" in texto, texto
assert fechados == ["calculadora"] and tars.conversa == conversa_antes and tars.ARQUIVO_SESSOES == arquivo_antes
relatorio = open(os.environ["TARS_ARQUIVO_AUTOTESTE"], encoding="utf-8").read()
assert "passou" in relatorio and "\033[" not in relatorio and "frase x | busca | geral" in relatorio
print("OK: IA clicou 7 + 2 =, o visor mostra 9: passou (relatório em texto puro, histórico intacto)")

# 3. A IA diz "9", mas o visor não mostra: não passa
visor["textos"] = ["7+"]
codigo, texto = rodar(completo=True)
assert codigo == 1 and "não chegou no 9 (visor: 7+)" in texto, texto
print("OK: a IA dizer '9' não basta: o teste confere o visor")

# 4. Diagnóstico rápido não abre nada
tars.processar_mensagem = lambda texto: (_ for _ in ()).throw(AssertionError("o diagnóstico não pode chamar a IA"))
codigo, texto = rodar(completo=False)
assert "Calculadora 7 + 2" not in texto and "Clique pelo nome" in texto and "gnome-calculator" in texto, texto
print("OK: --diagnostico só confere o ambiente (não abre a calculadora)")

print("\nTUDO OK.")