mirror of
https://github.com/emqx/neuron.git
synced 2026-10-18 13:41:22 +08:00
131 lines
5.1 KiB
Python
131 lines
5.1 KiB
Python
import json
|
|
from pathlib import Path
|
|
import socket
|
|
import time
|
|
|
|
import neuron.api as api
|
|
import neuron.error as error
|
|
import neuron.config as config
|
|
from neuron.common import *
|
|
|
|
|
|
def _update_build_neuron_json(port):
|
|
roots = [Path("build"), Path(__file__).parents[3] / "build"]
|
|
for path in (r / s / "neuron.json" for r in roots for s in ["config", ""]):
|
|
if path.exists():
|
|
with path.open("r+", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
sim = data.get("modbus_simulator") or {}
|
|
sim["port"] = int(port)
|
|
if not sim.get("ip") or sim["ip"] == "0.0.0.0":
|
|
sim["ip"] = "127.0.0.1"
|
|
data["modbus_simulator"] = sim
|
|
f.seek(0)
|
|
json.dump(data, f, ensure_ascii=False, indent=1)
|
|
f.truncate()
|
|
return
|
|
assert False, "neuron.json not found"
|
|
|
|
class TestSimulator:
|
|
|
|
def _check_status(self, running=True, port=None, tag_count=None):
|
|
st = api.simulator_status().json()
|
|
assert set(st.keys()) >= {"running", "port", "tag_count", "error"}
|
|
assert st["error"] == 0
|
|
assert st["running"] == (1 if running else 0)
|
|
if port is not None:
|
|
assert st["port"] == port
|
|
if tag_count is not None:
|
|
assert st["tag_count"] == tag_count
|
|
return st
|
|
|
|
@description(given="load neuron.json",
|
|
when="start and stop simulator",
|
|
then="status reflects running and correct port")
|
|
def test_start_stop_status(self, class_setup_and_teardown):
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
s.bind(("127.0.0.1", 0))
|
|
sim_port = s.getsockname()[1]
|
|
_update_build_neuron_json(sim_port)
|
|
class_setup_and_teardown.restart()
|
|
|
|
api.simulator_start()
|
|
self._check_status(running=True, port=sim_port, tag_count=0)
|
|
|
|
api.simulator_stop()
|
|
self._check_status(running=False, port=sim_port)
|
|
|
|
@description(given="set three tags using full name/address strings",
|
|
when="list tags",
|
|
then="returned tags")
|
|
def test_set_config_and_list_tags(self, class_setup_and_teardown):
|
|
api.simulator_start()
|
|
|
|
tags = [
|
|
{"type": "sine", "name": "t1", "address": "1!400001"},
|
|
{"type": "square", "name": "t3", "address": "1!400005"},
|
|
{"type": "saw", "name": "t2", "address": "1!400010"},
|
|
]
|
|
api.simulator_set_config(tags)
|
|
|
|
lt = api.simulator_list_tags().json()
|
|
assert "tags" in lt
|
|
assert isinstance(lt["tags"], list) and len(lt["tags"]) == 3
|
|
names = [t["name"] for t in lt["tags"]]
|
|
addrs = [t["address"] for t in lt["tags"]]
|
|
types = [t["type"] for t in lt["tags"]]
|
|
assert addrs == ["1!400001", "1!400005", "1!400010"]
|
|
assert names == ["t1", "t3", "t2"]
|
|
assert types == ["sine", "square", "saw"]
|
|
|
|
@description(given="simulator configured and running",
|
|
when="export configuration",
|
|
then="exports as attachment with expected structure and correct port")
|
|
def test_export_attachment(self):
|
|
st = api.simulator_status().json()
|
|
port = st.get("port", 0)
|
|
resp = api.simulator_export()
|
|
assert 200 == resp.status_code
|
|
dispo = resp.headers.get("Content-Disposition", "")
|
|
assert "attachment;" in dispo
|
|
assert "simulator_modbus_south.json" in dispo
|
|
|
|
data = json.loads(resp.text)
|
|
assert "nodes" in data and isinstance(data["nodes"], list)
|
|
assert len(data["nodes"]) >= 1
|
|
node0 = data["nodes"][0]
|
|
assert "plugin" in node0 and node0["plugin"] == "Modbus TCP"
|
|
assert "params" in node0 and isinstance(node0["params"], dict)
|
|
assert node0["params"].get("port") == port
|
|
assert "groups" in node0 and isinstance(node0["groups"], list) and len(node0["groups"]) >= 1
|
|
assert node0["groups"][0].get("group") == "group1"
|
|
|
|
@description(given="simulator started and tags configured",
|
|
when="restart neuron with new port in config",
|
|
then="enabled state and tags restored from DB, port updated from neuron.json")
|
|
def test_persistence_after_restart(self, class_setup_and_teardown):
|
|
api.simulator_start()
|
|
tags = [{"type": "sine", "name": "p1", "address": "1!400001"},
|
|
{"type": "sine", "name": "p2", "address": "1!400011"}]
|
|
api.simulator_set_config(tags)
|
|
|
|
st_before = self._check_status(running=True, tag_count=2)
|
|
port_before = st_before["port"]
|
|
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
s.bind(("127.0.0.1", 0))
|
|
new_port = s.getsockname()[1]
|
|
if new_port == port_before:
|
|
new_port += 1
|
|
|
|
_update_build_neuron_json(new_port)
|
|
|
|
class_setup_and_teardown.restart()
|
|
|
|
self._check_status(running=True, port=new_port, tag_count=2)
|
|
|
|
lt = api.simulator_list_tags().json()
|
|
assert len(lt["tags"]) == 2
|
|
assert lt["tags"][0]["name"] == "p1"
|
|
assert lt["tags"][1]["name"] == "p2"
|