From efba2bd1a88602a9b33ebbc97c47436897586cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Galland?= Date: Sat, 6 Oct 2018 13:38:01 +0200 Subject: [PATCH] [core] Add the function at() into the Schedules BIC. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Galland --- .../io.sarl.core/src/io/sarl/core/bic.sarl | 29 ++++ .../kernel/bic/SchedulesSkill.java | 23 +++ .../tests/kernel/bic/SchedulesSkillTest.java | 155 +++++++++++++++++- 3 files changed, 202 insertions(+), 5 deletions(-) diff --git a/main/apiplugins/io.sarl.core/src/io/sarl/core/bic.sarl b/main/apiplugins/io.sarl.core/src/io/sarl/core/bic.sarl index 66c4f06d04..9048fd5bc8 100644 --- a/main/apiplugins/io.sarl.core/src/io/sarl/core/bic.sarl +++ b/main/apiplugins/io.sarl.core/src/io/sarl/core/bic.sarl @@ -687,6 +687,35 @@ capacity Schedules { */ def execute(task : AgentTask = null, procedure : (Agent) => void ) : AgentTask + /** + * Schedule a given task to be executed at the given time. + * + *

If the given time is passed, according to {@link Time} then the task is not executed. + * + *

The given procedure takes one parameter: the agent associated to the task. It is name it by default. + * + *

The given time is expressed in milliseconds according to the time scale of the SRE. + * It means that the given number of milliseconds may be not real milliseconds, depending + * on the definition of the builtin capacity {@link Time}. + * + *

If this function is invoked from a {@code Behavior} and there is no provided task, + * the created task will be associated to the behavior instance. It means that the task will + * be automatically canceled when the behavior instance is unregistered from the the owning agent. + * + *

This function does nothing if one of the following conditions evaluates to true: + *

+ * + * @param task the task that will run the given closure. If null, a new task is created. + * @param time the time in milliseconds at which to start the procedure execution. + * @param procedure the closure to execute. + * @return the task is given, or a new task if the procedure is schedule, or + * {@code null} if the procedure is not scheduled. + * @since 0.9 + */ + def at(task : AgentTask = null, time : long, procedure : (Agent) => void) : AgentTask + } diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/janusproject/kernel/bic/SchedulesSkill.java b/sre/io.janusproject/io.janusproject.plugin/src/io/janusproject/kernel/bic/SchedulesSkill.java index 2d5dde0d96..598aafe528 100644 --- a/sre/io.janusproject/io.janusproject.plugin/src/io/janusproject/kernel/bic/SchedulesSkill.java +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/janusproject/kernel/bic/SchedulesSkill.java @@ -49,6 +49,7 @@ import io.sarl.core.AgentTask; import io.sarl.core.Logging; import io.sarl.core.Schedules; +import io.sarl.core.Time; import io.sarl.lang.core.Agent; import io.sarl.lang.core.AgentTrait; import io.sarl.lang.core.Behavior; @@ -79,6 +80,8 @@ public class SchedulesSkill extends BuiltinSkill implements Schedules { private ClearableReference skillBufferLogging; + private ClearableReference skillBufferTime; + /** Constructor. * @param agent the owner of this skill. */ @@ -97,6 +100,17 @@ protected final Logging getLoggingSkill() { return $castSkill(Logging.class, this.skillBufferLogging); } + /** Replies the Time skill as fast as possible. + * + * @return the skill + */ + protected final Time getTimeSkill() { + if (this.skillBufferTime == null || this.skillBufferTime.get() == null) { + this.skillBufferTime = $getSkill(Time.class); + } + return $castSkill(Time.class, this.skillBufferTime); + } + /** Replies the mutex for synchronizing on the task list. * * @return the mutex. @@ -232,6 +246,15 @@ public AgentTask in(AgentTask task, long delay, Procedure1 proced return pair.getTask(); } + @Override + public AgentTask at(AgentTask task, long time, Procedure1 procedure) { + final long delay = Math.round(time - getTimeSkill().getTime()); + if (delay > 0.) { + return in(task, delay, procedure); + } + return task; + } + private TaskDescription preRunTask(AgentTask task, Procedure1 procedure) { final TaskDescription pair; final AgentTask rtask; diff --git a/sre/io.janusproject/io.janusproject.tests/src/test/java/io/janusproject/tests/kernel/bic/SchedulesSkillTest.java b/sre/io.janusproject/io.janusproject.tests/src/test/java/io/janusproject/tests/kernel/bic/SchedulesSkillTest.java index 01b22bb3c8..2c500c45a9 100644 --- a/sre/io.janusproject/io.janusproject.tests/src/test/java/io/janusproject/tests/kernel/bic/SchedulesSkillTest.java +++ b/sre/io.janusproject/io.janusproject.tests/src/test/java/io/janusproject/tests/kernel/bic/SchedulesSkillTest.java @@ -26,6 +26,8 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; + import java.util.Collection; import java.util.UUID; import java.util.concurrent.ScheduledFuture; @@ -50,6 +52,7 @@ import org.mockito.stubbing.Answer; import io.janusproject.kernel.bic.SchedulesSkill; +import io.janusproject.kernel.bic.TimeSkill; import io.janusproject.services.executor.ExecutorService; import io.janusproject.services.logging.LogService; import io.janusproject.tests.testutils.AbstractJanusRunTest; @@ -61,12 +64,15 @@ import io.sarl.core.Initialize; import io.sarl.core.Lifecycle; import io.sarl.core.Schedules; +import io.sarl.core.Time; import io.sarl.lang.SARLVersion; import io.sarl.lang.annotation.PerceptGuardEvaluator; import io.sarl.lang.annotation.SarlSpecification; import io.sarl.lang.core.Agent; import io.sarl.lang.core.Behavior; import io.sarl.lang.core.BuiltinCapacitiesProvider; +import io.sarl.lang.core.SREutils; +import io.sarl.lang.core.Skill; import io.sarl.lang.core.Skill.UninstallationStage; import io.sarl.tests.api.Nullable; @@ -89,22 +95,72 @@ public static class StaticTests extends AbstractJanusTest { @Nullable private UUID agentId; + @InjectMocks + private SchedulesSkill skill; + @Mock private ExecutorService executorService; @Mock + private LogService logger; + private Agent agent; - @Mock - private LogService logger; + private Skill timeSkill; - @InjectMocks - private SchedulesSkill skill; + /** + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static class TimeSkillMock extends Skill implements Time { + + public TimeSkillMock(Agent agent) { + super(agent); + } + @Override + public double getTime(TimeUnit timeUnit) { + return 0; + } + + @Override + public double getOSTimeFactor() { + return 1; + } + + @Override + public double toOSTime(double timeValue) { + return timeValue; + } + + @Override + public double fromOSTime(double timeValue) { + return timeValue; + } + + @Override + public double toOSDuration(double timeDuration) { + return timeDuration; + } + + @Override + public double fromOSDuration(double timeDuration) { + return timeDuration; + } + + } + @Before public void setUp() throws Exception { this.agentId = UUID.randomUUID(); - Mockito.when(this.agent.getID()).thenReturn(this.agentId); + this.agent = new Agent(UUID.randomUUID(), this.agentId); + this.agent = spy(this.agent); + this.timeSkill = new TimeSkillMock(this.agent); + this.timeSkill = spy(this.timeSkill); + SREutils.createSkillMapping(this.agent, Time.class, this.timeSkill); + this.reflect.invoke(this.skill, "setOwner", this.agent); Mockito.when(this.executorService.schedule(ArgumentMatchers.any(Runnable.class), ArgumentMatchers.any(long.class), ArgumentMatchers.any(TimeUnit.class))).thenAnswer(new Answer() { @Override @@ -235,6 +291,95 @@ public void everyAgentTaskLongProcedure1() { assertSame(TimeUnit.MILLISECONDS, argument4.getValue()); } + @Test + public void atLongProcedure1_inTheFuture() { + Procedure1 procedure = Mockito.mock(Procedure1.class); + AgentTask task = this.skill.at(5, procedure); + assertNotNull(task); + assertSame(procedure, task.getProcedure()); + ArgumentCaptor argument1 = ArgumentCaptor.forClass(Runnable.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor argument3 = ArgumentCaptor.forClass(TimeUnit.class); + Mockito.verify(this.executorService, new Times(1)).schedule(argument1.capture(), argument2.capture(), + argument3.capture()); + assertNotNull(argument1.getValue()); + assertEquals(new Long(5), argument2.getValue()); + assertSame(TimeUnit.MILLISECONDS, argument3.getValue()); + } + + @Test + public void atLongProcedure1_now() { + Procedure1 procedure = Mockito.mock(Procedure1.class); + AgentTask task = this.skill.at(0, procedure); + assertNull(task); + ArgumentCaptor argument1 = ArgumentCaptor.forClass(Runnable.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor argument3 = ArgumentCaptor.forClass(TimeUnit.class); + Mockito.verify(this.executorService, never()).schedule(argument1.capture(), argument2.capture(), + argument3.capture()); + } + + @Test + public void atLongProcedure1_inThePast() { + Procedure1 procedure = Mockito.mock(Procedure1.class); + AgentTask task = this.skill.at(-5, procedure); + assertNull(task); + ArgumentCaptor argument1 = ArgumentCaptor.forClass(Runnable.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor argument3 = ArgumentCaptor.forClass(TimeUnit.class); + Mockito.verify(this.executorService, never()).schedule(argument1.capture(), argument2.capture(), + argument3.capture()); + } + + @Test + public void atAgentTaskLongProcedure1_inTheFuture() { + AgentTask task = Mockito.mock(AgentTask.class); + Mockito.when(task.getName()).thenReturn("thetask"); //$NON-NLS-1$ + Procedure1 procedure = Mockito.mock(Procedure1.class); + AgentTask t = this.skill.at(task, 5, procedure); + assertSame(task, t); + ArgumentCaptor argument0 = ArgumentCaptor.forClass(Procedure1.class); + Mockito.verify(task, new Times(1)).setProcedure(argument0.capture()); + assertSame(procedure, argument0.getValue()); + ArgumentCaptor argument1 = ArgumentCaptor.forClass(Runnable.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor argument3 = ArgumentCaptor.forClass(TimeUnit.class); + Mockito.verify(this.executorService, new Times(1)).schedule(argument1.capture(), argument2.capture(), + argument3.capture()); + assertNotNull(argument1.getValue()); + assertEquals(new Long(5), argument2.getValue()); + assertSame(TimeUnit.MILLISECONDS, argument3.getValue()); + } + + @Test + public void atAgentTaskLongProcedure1_now() { + AgentTask task = Mockito.mock(AgentTask.class); + Mockito.when(task.getName()).thenReturn("thetask"); //$NON-NLS-1$ + Procedure1 procedure = Mockito.mock(Procedure1.class); + AgentTask t = this.skill.at(task, 0, procedure); + assertSame(task, t); + ArgumentCaptor argument1 = ArgumentCaptor.forClass(Runnable.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor argument3 = ArgumentCaptor.forClass(TimeUnit.class); + Mockito.verify(this.executorService, never()).schedule(argument1.capture(), argument2.capture(), + argument3.capture()); + } + + @Test + public void atAgentTaskLongProcedure1_inThePast() { + AgentTask task = Mockito.mock(AgentTask.class); + Mockito.when(task.getName()).thenReturn("thetask"); //$NON-NLS-1$ + Procedure1 procedure = Mockito.mock(Procedure1.class); + AgentTask t = this.skill.at(task, -5, procedure); + assertSame(task, t); + assertSame(task, t); + ArgumentCaptor argument1 = ArgumentCaptor.forClass(Runnable.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor argument3 = ArgumentCaptor.forClass(TimeUnit.class); + Mockito.verify(this.executorService, never()).schedule(argument1.capture(), argument2.capture(), + argument3.capture()); + } + @Test public void uninstall() throws Exception { Procedure1 procedure1 = Mockito.mock(Procedure1.class);