mosaicode/tests/test_base.py

383 lines
12 KiB
Python

# -*- coding: utf-8 -*-
"""
Base test class for mosaicode tests.
Updated for new architecture with Pydantic models and JSON configuration.
"""
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
from gi.repository import GLib
import unittest
from time import sleep
from abc import ABCMeta
from pathlib import Path
from typing import Dict, List, Optional, Any
import tempfile
import shutil
import json
# Mosaicode imports
from mosaicode.GUI.block import Block
from mosaicode.GUI.comment import Comment
from mosaicode.GUI.diagram import Diagram
from mosaicode.GUI.mainwindow import MainWindow
from mosaicode.GUI.fieldtypes import *
from mosaicode.model.blockmodel import BlockModel
from mosaicode.model.port import Port
from mosaicode.model.codetemplate import CodeTemplate
from mosaicode.model.connectionmodel import ConnectionModel
from mosaicode.control.diagramcontrol import DiagramControl
from mosaicode.control.blockcontrol import BlockControl
from mosaicode.system import System
from mosaicode.utils.pydantic_schemas import BlockDefaultValues, PortDefaultValues
class TestBase(unittest.TestCase):
"""Base test class with updated helpers for new mosaicode architecture."""
__metaclass__ = ABCMeta
def setUp(self):
"""Set up test environment."""
# Create temporary test directory
self.test_dir = Path(tempfile.mkdtemp(prefix="mosaicode_test_"))
# Initialize system for testing
System()
System.reload()
def tearDown(self):
"""Clean up test environment."""
# Remove temporary test directory
if self.test_dir.exists():
shutil.rmtree(self.test_dir)
def refresh_gui(self, delay=0):
"""Process GTK events and optionally wait."""
while Gtk.events_pending():
Gtk.main_iteration_do(False)
sleep(delay)
def create_main_window(self):
"""Create a main window for testing."""
return MainWindow()
def create_diagram(self, main_window=None):
"""Create a test diagram."""
if main_window is None:
main_window = self.create_main_window()
diagram = Diagram(main_window)
diagram.language = "Test"
diagram.zoom = 2
diagram.code_template = self.create_code_template()
return diagram
def create_full_diagram(self):
"""Create a complete diagram with blocks and connections."""
main_window = self.create_main_window()
diagram = Diagram(main_window)
diagram_control = self.create_diagram_control()
diagram = diagram_control.diagram
block1 = self.create_block()
print(f"[DEBUG] Adicionando block1 ao diagrama: valor={block1}, tipo={type(block1)}")
diagram_control.add_block(block1)
block2 = self.create_block()
print(f"[DEBUG] Adicionando block2 ao diagrama: valor={block2}, tipo={type(block2)}")
diagram_control.add_block(block2)
# Ensure blocks have enough ports before creating connections
if len(block1.ports) >= 2 and len(block2.ports) >= 2:
connection = ConnectionModel(
diagram,
block1,
block1.ports[0],
block2,
block2.ports[1]
)
diagram_control.add_connection(connection)
connection = ConnectionModel(
diagram,
block1,
block1.ports[1],
block2,
block2.ports[0]
)
diagram_control.add_connection(connection)
# Removed problematic connection with None values
comment = self.create_comment()
main_window.main_control.add_comment(comment)
diagram_control.add_comment(comment)
return diagram
def create_diagram_control(self, diagram=None):
"""Create a diagram control for testing."""
if diagram is None:
diagram = self.create_diagram()
diagram_control = DiagramControl(diagram)
diagram_control.connectors = []
diagram_control.language = "language"
return diagram_control
def create_block(self, diagram_control=None):
"""Create a test block with updated structure."""
if diagram_control is None:
diagram_control = self.create_diagram_control()
block_model = BlockModel()
# Create ports using new structure
port0 = Port()
port0.label = "Test0"
port0.conn_type = Port.OUTPUT
port0.name = "Test0"
port0.type = "Test"
port0.index = 0
port1 = Port()
port1.label = "Test1"
port1.conn_type = Port.INPUT
port1.name = "Test1"
port1.type = "Test"
port1.index = 1
block_model.ports = [port0, port1]
# Set block properties using new structure
block_model.help = "Test"
block_model.label = "Test"
block_model.color = "#C8C819" # Updated color format
block_model.group = "Test"
block_model.codes = {"code0":"Test",
"Code1":"Test",
"Code2":"Test"}
block_model.type = "Test"
block_model.language = "Test"
block_model.properties = [{"name": "test",
"label": "Test",
"value": "0",
"type": MOSAICODE_FLOAT
}]
block_model.extension = "Test"
block_model.file = None
block_model.id = len(System.get_blocks()) + 1 # Add unique ID
block = Block(diagram_control.diagram, block_model)
# Don't override block.id - let the Block constructor handle it
# Register the block in System.get_blocks() for persistence to work correctly
System.get_blocks()[block.type] = block
# Salva o bloco como extensão temporária reconhecida pelo mosaicode
from mosaicode.persistence.blockpersistence import BlockPersistence
import os
temp_dir = "mosaicode/extensions/blocks"
os.makedirs(temp_dir, exist_ok=True)
BlockPersistence.save(block, temp_dir)
from mosaicode.system import System as MosaicodeSystem
MosaicodeSystem.reload()
# DEBUG: Verificar tipo do bloco criado
print(f"[DEBUG] Bloco criado em create_block: valor={block}, tipo={type(block)}")
assert not isinstance(block, int), f"[ERRO] create_block retornou um inteiro: {block}"
return block
def create_comment(self):
"""Create a test comment."""
comment = Comment(self.create_diagram(), None)
return comment
def create_port(self):
"""Create a test port."""
port = Port()
return port
def create_connection(self):
"""Create a test connection."""
from mosaicode.model.connectionmodel import ConnectionModel
connection = ConnectionModel(
self.create_diagram(),
self.create_block(),
self.create_port(),
self.create_block(),
self.create_port()
)
return connection
def create_code_template(self):
"""Create a test code template with updated structure."""
code_template = CodeTemplate()
code_template.name = "webaudio"
code_template.type = "Test"
code_template.language = "javascript"
code_template.command = "python -m webbrowser -t $dir_name$index.html\n"
code_template.description = "Javascript / webaudio code template"
code_template.code_parts = ["onload", "function", "declaration", "execution", "html"]
code_template.properties = [{"name": "title",
"label": "Title",
"value": "Title",
"type": MOSAICODE_STRING
}
]
# Add code templates to the codes dictionary
code_template.codes["html"] = r"""
<html>
<head>
<meta http-equiv="Cache-Control" content="no-store" />
<!-- $author$ $license$ -->
<title>$prop[title]$</title>
<link rel="stylesheet" type="text/css" href="theme.css">
<script src="functions.js"></script>
<script>
$single_code[function]$
function loadme(){
$single_code[onload]$
return;
}
var context = new (window.AudioContext || window.webkitAudioContext)();
//declaration block
$code[declaration]$
//execution
$code[execution]$
//connections
$connections$
</script>
</head>
<body onload='loadme();'>
$code[html]$
</body>
</html>
"""
code_template.codes["css"] = r"""
/*
Developed by: $author$
*/
html, body {
background: #ffeead;
color: #ff6f69;
}
h1, p {
color: #ff6f69;
}
#navbar a {
color: #ff6f69;
}
.item {
background: #ffcc5c;
}
button {
background: #ff6f69;
color: #ffcc5c;
}
"""
code_template.codes["js"] = r"""
/*
Developed by: $author$
*/
$single_code[function]$
"""
System.get_code_templates()[code_template.type] = code_template
return code_template
def create_test_json_file(self, data: Dict[str, Any], filename: str) -> Path:
"""Create a temporary JSON file for testing."""
file_path = self.test_dir / filename
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
return file_path
def create_test_block_json(self) -> Dict[str, Any]:
"""Create test block data in JSON format."""
return {
"id": -1,
"version": "0.0.1",
"x": 0,
"y": 0,
"is_collapsed": False,
"type": "TestBlock",
"language": "javascript",
"extension": "test",
"file": None,
"help": "Test block for unit testing",
"label": "TestBlock",
"color": "#C8C819",
"group": "Test",
"ports": [
{
"type": "Test",
"label": "Output",
"conn_type": "OUTPUT",
"name": "output",
"index": 0
},
{
"type": "Test",
"label": "Input",
"conn_type": "INPUT",
"name": "input",
"index": 1
}
],
"maxIO": 2,
"properties": [
{
"name": "test_prop",
"label": "Test Property",
"value": "0",
"type": "float"
}
],
"codes": {
"code0": "// Test code",
"code1": "// Another test code"
},
"gen_codes": {},
"weight": 0,
"connections": []
}
def create_test_port_json(self) -> Dict[str, Any]:
"""Create test port data in JSON format."""
return {
"version": "0.0.1",
"type": "TestPort",
"language": "javascript",
"hint": "Test port hint",
"color": "#0000FF",
"multiple": False,
"code": "// Port code",
"var_name": "$block[label]$_$block[id]$_$port[name]$",
"conn_type": "INPUT",
"name": "test_port",
"label": "Test Port",
"index": 0,
"type_index": 0,
"file": None
}
def assert_valid_block_model(self, block: BlockModel):
"""Assert that a block model is valid according to new structure."""
self.assertIsInstance(block, BlockModel)
self.assertIsNotNone(block.type)
self.assertIsNotNone(block.label)
self.assertIsInstance(block.ports, list)
self.assertIsInstance(block.properties, list)
self.assertIsInstance(block.codes, dict)
def assert_valid_port_model(self, port: Port):
"""Assert that a port model is valid according to new structure."""
self.assertIsInstance(port, Port)
self.assertIsNotNone(port.type)
self.assertIsNotNone(port.name)
self.assertIn(port.conn_type, [Port.INPUT, Port.OUTPUT])