1
0
mirror of https://github.com/gsi-upm/soil synced 2025-08-23 19:52:19 +00:00
This commit is contained in:
J. Fernando Sánchez
2023-05-03 12:14:49 +02:00
parent 5e93399d58
commit f49be3af68
38 changed files with 913 additions and 836 deletions

View File

@@ -17,7 +17,7 @@ class TestAgents(TestCase):
"""The last step of a dead agent should return time.INFINITY"""
d = Dead(unique_id=0, model=environment.Environment())
ret = d.step()
assert ret == stime.NEVER
assert ret == stime.INFINITY
def test_die_raises_exception(self):
"""A dead agent should raise an exception if it is stepped after death"""
@@ -52,23 +52,25 @@ class TestAgents(TestCase):
def test_state_decorator(self):
class MyAgent(agents.FSM):
run = 0
times_run = 0
@agents.state("original", default=True)
def root(self):
self.run += 1
return self.other
@agents.state
def other(self):
self.run += 1
self.times_run += 1
e = environment.Environment()
a = e.add_agent(MyAgent)
e.step()
assert a.run == 1
assert a.times_run == 0
a.step()
print("DONE")
assert a.times_run == 1
assert a.state_id == MyAgent.other.id
a.step()
assert a.times_run == 2
def test_broadcast(self):
"""
@@ -86,7 +88,7 @@ class TestAgents(TestCase):
except Exception as ex:
print(ex)
while True:
self.check_messages()
self.process_messages()
yield
def on_receive(self, msg, sender=None):
@@ -132,14 +134,14 @@ class TestAgents(TestCase):
while True:
if pongs or not pings: # First agent, or anyone after that
pings.append(self.now)
response = yield target.ask("PING")
response = yield from target.ask("PING")
responses.append(response)
else:
print("NOT sending ping")
print("Checking msgs")
# Do not block if we have already received a PING
if not self.check_messages():
yield self.received()
if not self.process_messages():
yield from self.received()
print("done")
def on_receive(self, msg, sender=None):
@@ -174,4 +176,200 @@ class TestAgents(TestCase):
assert len(ev) == 1
assert ev[0].unique_id == 1
null = list(e.agents(unique_ids=[0, 1], agent_class=agents.NetworkAgent))
assert not null
assert not null
def test_agent_return(self):
'''
An agent should be able to cycle through different states and control when it
should be awaken.
'''
class TestAgent(agents.Agent):
@agents.state(default=True)
def one(self):
return self.two
@agents.state
def two(self):
return self.three.at(10)
@agents.state
def three(self):
return self.four.delay(1)
@agents.state
def four(self):
yield self.delay(2)
return self.five.delay(3)
@agents.state
def five(self):
return self.delay(1)
model = environment.Environment()
a = model.add_agent(TestAgent)
assert a.state_id == TestAgent.one.id
assert a.now == 0
model.step()
assert a.state_id == TestAgent.two.id
assert a.now == 1
model.step()
assert a.state_id == TestAgent.three.id
assert a.now == 10
model.step()
assert a.state_id == TestAgent.four.id
assert a.now == 11
model.step()
assert a.state_id == TestAgent.four.id
assert a.now == 13
model.step()
assert a.state_id == TestAgent.five.id
assert a.now == 16
model.step()
assert a.state_id == TestAgent.five.id
assert a.now == 17
def test_agent_async(self):
'''
Async functions should also be valid states.
'''
class TestAgent(agents.Agent):
@agents.state(default=True)
def one(self):
return self.two
@agents.state
def two(self):
return self.three.at(10)
@agents.state
def three(self):
return self.four.delay(1)
@agents.state
async def four(self):
await self.delay(2)
return self.five.delay(3)
@agents.state
def five(self):
return self.delay(1)
model = environment.Environment()
a = model.add_agent(TestAgent)
assert a.now == 0
assert a.state_id == TestAgent.one.id
model.step()
assert a.now == 1
assert a.state_id == TestAgent.two.id
model.step()
assert a.now == 10
assert a.state_id == TestAgent.three.id
model.step()
assert a.state_id == TestAgent.four.id
assert a.now == 11
model.step()
assert a.state_id == TestAgent.four.id
assert a.now == 13
model.step()
assert a.state_id == TestAgent.five.id
assert a.now == 16
model.step()
assert a.state_id == TestAgent.five.id
assert a.now == 17
def test_agent_return_step(self):
'''
The same result as the previous test should be achievable by manually
handling the agent state.
'''
class TestAgent(agents.Agent):
my_state = 1
my_count = 0
def step(self):
if self.my_state == 1:
self.my_state = 2
return None
elif self.my_state == 2:
self.my_state = 3
return self.at(10)
elif self.my_state == 3:
self.my_state = 4
self.my_count = 0
return self.delay(1)
elif self.my_state == 4:
self.my_count += 1
if self.my_count == 1:
return self.delay(2)
self.my_state = 5
return self.delay(3)
elif self.my_state == 5:
return self.delay(1)
model = environment.Environment()
a = model.add_agent(TestAgent)
assert a.my_state == 1
assert a.now == 0
model.step()
assert a.now == 1
assert a.my_state == 2
model.step()
assert a.now == 10
assert a.my_state == 3
model.step()
assert a.now == 11
assert a.my_state == 4
model.step()
assert a.now == 13
assert a.my_state == 4
model.step()
assert a.now == 16
assert a.my_state == 5
model.step()
assert a.now == 17
assert a.my_state == 5
def test_agent_return_step_async(self):
'''
The same result as the previous test should be achievable by manually
handling the agent state.
'''
class TestAgent(agents.Agent):
my_state = 1
async def step(self):
self.my_state = 2
await self.delay()
self.my_state = 3
await self.at(10)
self.my_state = 4
await self.delay(1)
await self.delay(2)
self.my_state = 5
await self.delay(3)
while True:
await self.delay(1)
model = environment.Environment()
a = model.add_agent(TestAgent)
assert a.my_state == 1
assert a.now == 0
model.step()
assert a.now == 1
assert a.my_state == 2
model.step()
assert a.now == 10
assert a.my_state == 3
model.step()
assert a.now == 11
assert a.my_state == 4
model.step()
assert a.now == 13
assert a.my_state == 4
model.step()
assert a.now == 16
assert a.my_state == 5
model.step()
assert a.now == 17
assert a.my_state == 5

View File

@@ -29,15 +29,12 @@ class TestConfig(TestCase):
def test_torvalds_config(self):
sim = simulation.from_config(os.path.join(ROOT, "test_config.yml"))
MAX_STEPS = 10
INTERVAL = 2
assert sim.interval == INTERVAL
assert sim.max_steps == MAX_STEPS
envs = sim.run()
assert len(envs) == 1
env = envs[0]
assert env.interval == 2
assert env.count_agents() == 3
assert env.now == INTERVAL * MAX_STEPS
assert env.now == MAX_STEPS
def make_example_test(path, cfg):

View File

@@ -1,5 +1,4 @@
---
source_file: "../examples/torvalds_sim.py"
model: "TorvaldsEnv"
max_steps: 10
interval: 2
max_steps: 10

View File

@@ -88,7 +88,7 @@ class Exporters(TestCase):
parameters=dict(
network_generator="complete_graph",
network_params={"n": n_nodes},
agent_class="CounterModel",
agent_class=agents.CounterModel,
agent_reporters={"times": "times"},
),
max_time=max_time,

View File

@@ -7,8 +7,6 @@ from functools import partial
from os.path import join
from soil import simulation, Environment, agents, network, serialization, utils, config, from_file
from soil.time import Delta
from mesa import Agent as MesaAgent
ROOT = os.path.abspath(os.path.dirname(__file__))
@@ -114,7 +112,6 @@ class TestMain(TestCase):
def test_serialize_class(self):
ser, name = serialization.serialize(agents.BaseAgent, known_modules=[])
assert name == "soil.agents.BaseAgent"
assert ser == agents.BaseAgent
ser, name = serialization.serialize(
agents.BaseAgent,
@@ -123,11 +120,9 @@ class TestMain(TestCase):
],
)
assert name == "BaseAgent"
assert ser == agents.BaseAgent
ser, name = serialization.serialize(CustomAgent)
assert name == "test_main.CustomAgent"
assert ser == CustomAgent
pickle.dumps(ser)
def test_serialize_builtin_types(self):
@@ -168,7 +163,6 @@ class TestMain(TestCase):
def test_fsm(self):
"""Basic state change"""
class ToggleAgent(agents.FSM):
@agents.default_state
@agents.state
@@ -193,7 +187,7 @@ class TestMain(TestCase):
@agents.default_state
@agents.state
def ping(self):
return self.pong, 2
return self.pong.delay(2)
@agents.state
def pong(self):
@@ -203,7 +197,7 @@ class TestMain(TestCase):
when = a.step()
assert when == 2
when = a.step()
assert when == Delta(a.interval)
assert when == None
def test_load_sim(self):
"""Make sure at least one of the examples can be loaded"""
@@ -232,4 +226,4 @@ class TestMain(TestCase):
assert len(configs) == len(a) * len(b)
for i in a:
for j in b:
assert {"a": i, "b": j} in configs
assert {"a": i, "b": j} in configs

View File

@@ -4,26 +4,6 @@ from soil import time, agents, environment
class TestMain(TestCase):
def test_cond(self):
"""
A condition should match a When if the concition is True
"""
t = time.Cond(lambda t: True)
f = time.Cond(lambda t: False)
for i in range(10):
w = time.When(i)
assert w == t
assert w is not f
def test_cond(self):
"""
Comparing a Cond to a Delta should always return False
"""
c = time.Cond(lambda t: False)
d = time.Delta(1)
assert c is not d
def test_cond_env(self):
""" """
@@ -36,11 +16,12 @@ class TestMain(TestCase):
class CondAgent(agents.BaseAgent):
def step(self):
nonlocal done
nonlocal done, times_started, times_asleep, times_awakened
times_started.append(self.now)
while True:
times_asleep.append(self.now)
yield time.Cond(lambda agent: agent.now >= 10, delta=2)
while self.now < 10:
yield self.delay(2)
times_awakened.append(self.now)
if self.now >= 10:
break
@@ -57,7 +38,6 @@ class TestMain(TestCase):
assert times_started == [0]
assert times_awakened == [10]
assert done == [10]
# The first time will produce the Cond.
assert env.schedule.steps == 6
assert len(times) == 6
@@ -65,11 +45,10 @@ class TestMain(TestCase):
times.append(env.now)
env.step()
assert times == [0, 2, 4, 6, 8, 10, 11]
assert times == [0, 2, 4, 6, 8, 10, 11, 12]
assert env.schedule.time == 13
assert times_started == [0, 11]
assert times_awakened == [10]
assert done == [10]
# Once more to yield the cond, another one to continue
assert env.schedule.steps == 7
assert len(times) == 7
assert times_started == [0, 11, 12]
assert times_awakened == [10, 11, 12]
assert done == [10, 11, 12]
assert env.schedule.steps == 8
assert len(times) == 8