Merge pull request 'fix(ci): simplify Flutter test execution' (#9) from fix/simplify-ci-tests into main
Build & Deploy PluriWave / Análisis de código (push) Successful in 41s
Build & Deploy PluriWave / Build APK + AAB release (push) Failing after 2m29s

Reviewed-on: #9
This commit was merged in pull request #9.
This commit is contained in:
2026-05-31 00:11:44 +02:00
+3 -207
View File
@@ -31,217 +31,13 @@ jobs:
run: flutter analyze --no-fatal-infos --no-fatal-warnings run: flutter analyze --no-fatal-infos --no-fatal-warnings
- name: Ejecutar tests criticos - name: Ejecutar tests criticos
timeout-minutes: 4 timeout-minutes: 15
run: | run: |
python3 - <<'PY' flutter test test/servicios/servicio_programacion_alarmas_test.dart test/estado/estado_alarmas_test.dart --concurrency=1 --timeout=60s
import os
import signal
import subprocess
import sys
import tempfile
import time
test_paths = [
'test/servicios/servicio_programacion_alarmas_test.dart',
'test/estado/estado_alarmas_test.dart',
]
use_process_group = hasattr(os, 'killpg') and os.name != 'nt'
workspace = os.getcwd()
process_patterns = (
'flutter_tester',
'flutter_tools.snapshot',
'frontend_server',
'dartvm',
'dartaotruntime',
)
def kill_process_group(pid):
if not use_process_group:
return
try:
os.killpg(pid, signal.SIGTERM)
time.sleep(2)
os.killpg(pid, signal.SIGKILL)
except ProcessLookupError:
pass
def cleanup_flutter_processes():
ps = subprocess.run(
['ps', '-axww', '-o', 'pid=', '-o', 'command='],
text=True,
stdout=subprocess.PIPE,
check=False,
)
current_pid = os.getpid()
killed = []
for raw in ps.stdout.splitlines():
line = raw.strip()
if not line:
continue
pid_text, _, command = line.partition(' ')
try:
pid = int(pid_text)
except ValueError:
continue
if pid == current_pid:
continue
if workspace not in command and not any(path in command for path in test_paths):
continue
if not any(pattern in command for pattern in process_patterns + tuple(test_paths)):
continue
try:
os.kill(pid, signal.SIGTERM)
killed.append(pid)
except ProcessLookupError:
pass
if killed:
print('Procesos Flutter de test terminados: ' + ', '.join(map(str, killed)), flush=True)
time.sleep(1)
for pid in killed:
try:
os.kill(pid, signal.SIGKILL)
except ProcessLookupError:
pass
def run_test_file(path):
cmd = [
'flutter',
'test',
'--no-pub',
'--concurrency=1',
'--timeout=30s',
'--reporter=expanded',
path,
]
print('$ ' + ' '.join(cmd), flush=True)
output_path = None
try:
with tempfile.NamedTemporaryFile(delete=False) as tmp:
output_path = tmp.name
with open(output_path, 'w', encoding='utf-8', errors='replace') as writer:
process = subprocess.Popen(
cmd,
stdout=writer,
stderr=subprocess.STDOUT,
start_new_session=use_process_group,
)
deadline = time.monotonic() + 90
success_deadline = None
def flush_new_output():
nonlocal success_deadline
with open(output_path, 'r', encoding='utf-8', errors='replace') as reader:
reader.seek(flush_new_output.offset)
chunk = reader.read()
flush_new_output.offset = reader.tell()
if not chunk:
return
print(chunk, end='', flush=True)
if 'All tests passed!' in chunk and success_deadline is None:
success_deadline = time.monotonic() + 5
flush_new_output.offset = 0
while True:
flush_new_output()
returncode = process.poll()
if returncode is not None:
flush_new_output()
cleanup_flutter_processes()
return returncode
if success_deadline is not None and time.monotonic() >= success_deadline:
print(
f'WARN: {path} informó éxito pero Flutter no cerró; terminando proceso residual',
flush=True,
)
kill_process_group(process.pid)
flush_new_output()
cleanup_flutter_processes()
return 0
if time.monotonic() >= deadline:
print(f'ERROR: timeout ejecutando test critico {path}', file=sys.stderr, flush=True)
kill_process_group(process.pid)
flush_new_output()
cleanup_flutter_processes()
return 124
time.sleep(1)
finally:
if output_path:
try:
os.unlink(output_path)
except FileNotFoundError:
pass
cleanup_flutter_processes()
for path in test_paths:
code = run_test_file(path)
if code != 0:
sys.exit(code)
sys.exit(0)
PY
- name: Limpiar procesos Flutter de tests - name: Limpiar procesos Flutter de tests
if: always() if: always()
run: | run: pkill -f 'flutter_tester|flutter_tools.snapshot|dartaotruntime' 2>/dev/null || true
python3 - <<'PY'
import os
import signal
import subprocess
import time
workspace = os.getcwd()
test_paths = (
'test/estado/estado_alarmas_test.dart',
'test/servicios/servicio_programacion_alarmas_test.dart',
)
patterns = (
'flutter_tester',
'flutter_tools.snapshot',
'frontend_server',
'dartvm',
'dartaotruntime',
) + test_paths
current_pid = os.getpid()
ps = subprocess.run(
['ps', '-axww', '-o', 'pid=', '-o', 'command='],
text=True,
stdout=subprocess.PIPE,
check=False,
)
killed = []
for raw in ps.stdout.splitlines():
line = raw.strip()
if not line:
continue
pid_text, _, command = line.partition(' ')
try:
pid = int(pid_text)
except ValueError:
continue
if pid == current_pid:
continue
if workspace not in command and not any(path in command for path in test_paths):
continue
if not any(pattern in command for pattern in patterns):
continue
try:
os.kill(pid, signal.SIGTERM)
killed.append(pid)
except ProcessLookupError:
pass
if killed:
print('Procesos Flutter de test terminados: ' + ', '.join(map(str, killed)))
time.sleep(1)
for pid in killed:
try:
os.kill(pid, signal.SIGKILL)
except ProcessLookupError:
pass
else:
print('No quedaron procesos Flutter de test vivos')
PY
build: build:
name: Build APK + AAB release name: Build APK + AAB release