mirror of
https://github.com/Eyre-S/Coeur-Morny-Cono.git
synced 2024-11-25 12:37:39 +08:00
add CronTask, tests optimize
- add lib cron-utils: v9.2.0 - add CronTask - add CronTask's test - change MedicationTimer using cron as time calculation backend (not using CronTask) - change OnQuestionMarkReply support `⸘` - minor SchedulerTest "immediately" test logic changes
This commit is contained in:
parent
89c414e853
commit
3d44972233
@ -90,6 +90,7 @@ dependencies {
|
||||
implementation group: 'com.softwaremill.sttp.client3', name: scala('okhttp-backend'), version: lib_sttp_v
|
||||
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: lib_okhttp_v
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: lib_gson_v
|
||||
implementation group: 'com.cronutils', name: 'cron-utils', version: lib_cron_utils_v
|
||||
|
||||
testImplementation group: 'org.scalatest', name: scala('scalatest'), version: lib_scalatest_v
|
||||
testImplementation group: 'org.scalatest', name: scala('scalatest-freespec'), version: lib_scalatest_v
|
||||
|
@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur
|
||||
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
||||
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
|
||||
|
||||
VERSION = 1.3.0-dev1
|
||||
VERSION = 1.3.0-dev2
|
||||
|
||||
USE_DELTA = false
|
||||
VERSION_DELTA =
|
||||
@ -25,5 +25,6 @@ lib_javatelegramapi_v = 6.2.0
|
||||
lib_sttp_v = 3.9.0
|
||||
lib_okhttp_v = 4.11.0
|
||||
lib_gson_v = 2.10.1
|
||||
lib_cron_utils_v = 9.2.0
|
||||
|
||||
lib_scalatest_v = 3.2.17
|
||||
|
@ -4,7 +4,6 @@ import cc.sukazyo.cono.morny.bot.api.{EventEnv, EventListener}
|
||||
import cc.sukazyo.cono.morny.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.bot.event.OnQuestionMarkReply.isAllMessageMark
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
|
||||
import scala.language.postfixOps
|
||||
@ -33,7 +32,9 @@ class OnQuestionMarkReply (using coeur: MornyCoeur) extends EventListener {
|
||||
|
||||
object OnQuestionMarkReply {
|
||||
|
||||
private val QUESTION_MARKS = Set('?', '?', '¿', '⁈', '⁇', '‽', '❔', '❓')
|
||||
// todo: due to the limitation of Java char, the ⁉️ character (actually not a
|
||||
// single character) is not supported yet.
|
||||
private val QUESTION_MARKS = Set('?', '?', '¿', '⁈', '⁇', '‽', '⸘', '❔', '❓')
|
||||
|
||||
def isAllMessageMark (using text: String): Boolean = {
|
||||
boundary[Boolean] {
|
||||
|
@ -7,11 +7,14 @@ import cc.sukazyo.cono.morny.util.schedule.RoutineTask
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.CommonFormat
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||
import com.cronutils.builder.CronBuilder
|
||||
import com.cronutils.model.definition.{CronDefinition, CronDefinitionBuilder}
|
||||
import com.cronutils.model.time.ExecutionTime
|
||||
import com.pengrad.telegrambot.model.{Message, MessageEntity}
|
||||
import com.pengrad.telegrambot.request.{EditMessageText, SendMessage}
|
||||
import com.pengrad.telegrambot.response.SendResponse
|
||||
|
||||
import java.time.{LocalDateTime, ZoneOffset}
|
||||
import java.time.{Instant, ZonedDateTime, ZoneOffset}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.language.implicitConversions
|
||||
|
||||
@ -85,18 +88,24 @@ class MedicationTimer (using coeur: MornyCoeur) {
|
||||
|
||||
object MedicationTimer {
|
||||
|
||||
//noinspection ScalaWeakerAccess
|
||||
val cronDef: CronDefinition = CronDefinitionBuilder.defineCron
|
||||
.withHours.and
|
||||
.instance
|
||||
|
||||
@throws[IllegalArgumentException]
|
||||
def calcNextRoutineTimestamp (baseTimeMillis: EpochMillis, zone: ZoneOffset, notifyAt: Set[Int]): EpochMillis = {
|
||||
if (notifyAt isEmpty) throw new IllegalArgumentException("notify time is not set")
|
||||
var time = LocalDateTime.ofEpochSecond(
|
||||
baseTimeMillis / 1000, ((baseTimeMillis % 1000) * 1000 * 1000) toInt,
|
||||
zone
|
||||
).withMinute(0).withSecond(0).withNano(0)
|
||||
time = time plusHours 1
|
||||
while (!(notifyAt contains(time getHour))) {
|
||||
time = time plusHours 1
|
||||
}
|
||||
(time toInstant zone) toEpochMilli
|
||||
import com.cronutils.model.field.expression.FieldExpressionFactory.*
|
||||
ExecutionTime.forCron(CronBuilder.cron(cronDef)
|
||||
.withHour(and({
|
||||
import scala.jdk.CollectionConverters.*
|
||||
(for (i <- notifyAt) yield on(i)).toList.asJava
|
||||
}))
|
||||
.instance
|
||||
).nextExecution(
|
||||
ZonedDateTime ofInstant (Instant ofEpochMilli baseTimeMillis, zone.normalized)
|
||||
).get.toInstant.toEpochMilli
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
package cc.sukazyo.cono.morny.util.schedule
|
||||
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||
import com.cronutils.model.time.ExecutionTime
|
||||
import com.cronutils.model.Cron
|
||||
|
||||
import java.time.{Instant, ZonedDateTime, ZoneId}
|
||||
import scala.jdk.OptionConverters.*
|
||||
|
||||
trait CronTask extends RoutineTask {
|
||||
|
||||
private transparent inline def cronCalc = ExecutionTime.forCron(cron)
|
||||
|
||||
def cron: Cron
|
||||
|
||||
def zone: ZoneId
|
||||
|
||||
override def firstRoutineTimeMillis: EpochMillis =
|
||||
cronCalc.nextExecution(
|
||||
ZonedDateTime.ofInstant(
|
||||
Instant.now, zone
|
||||
)
|
||||
).get.toInstant.toEpochMilli
|
||||
|
||||
override def nextRoutineTimeMillis (previousRoutineScheduledTimeMillis: EpochMillis): EpochMillis | Null =
|
||||
cronCalc.nextExecution(
|
||||
ZonedDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(previousRoutineScheduledTimeMillis),
|
||||
zone
|
||||
)
|
||||
).toScala match
|
||||
case Some(time) => time.toInstant.toEpochMilli
|
||||
case None => null
|
||||
|
||||
}
|
||||
|
||||
object CronTask {
|
||||
|
||||
def apply (_name: String, _cron: Cron, _zone: ZoneId, _main: =>Unit): CronTask =
|
||||
new CronTask:
|
||||
override def name: String = _name
|
||||
override def cron: Cron = _cron
|
||||
override def zone: ZoneId = _zone
|
||||
override def main: Unit = _main
|
||||
|
||||
}
|
@ -16,10 +16,19 @@ class OnQuestionMarkReplyTest extends MornyTests with TableDrivenPropertyChecks
|
||||
("为什么?", false),
|
||||
("?这不合理", false),
|
||||
("??尊嘟假嘟", false),
|
||||
(":¿", false),
|
||||
("?????", true),
|
||||
("¿", true),
|
||||
("⁈??", true),
|
||||
("?!??", false),
|
||||
("⁇", true),
|
||||
("‽", true),
|
||||
("?⸘?", true),
|
||||
("?", true),
|
||||
("?", true),
|
||||
("??❔", true),
|
||||
("??", true),
|
||||
("❔", true),
|
||||
("❓❓❓", true),
|
||||
// ("⁉️", true)
|
||||
)
|
||||
forAll(examples) { (text, is) =>
|
||||
|
||||
|
@ -0,0 +1,51 @@
|
||||
package cc.sukazyo.cono.morny.test.utils.schedule
|
||||
|
||||
import cc.sukazyo.cono.morny.test.MornyTests
|
||||
import cc.sukazyo.cono.morny.util.schedule.{CronTask, Scheduler}
|
||||
import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
|
||||
import com.cronutils.builder.CronBuilder
|
||||
import com.cronutils.model.definition.CronDefinitionBuilder
|
||||
import com.cronutils.model.field.expression.FieldExpressionFactory as C
|
||||
import com.cronutils.model.time.ExecutionTime
|
||||
import org.scalatest.tagobjects.Slow
|
||||
|
||||
import java.lang.System.currentTimeMillis
|
||||
import java.time.{ZonedDateTime, ZoneOffset}
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
class CronTaskTest extends MornyTests {
|
||||
|
||||
"cron task works fine" taggedAs Slow in {
|
||||
|
||||
val scheduler = Scheduler()
|
||||
val cronSecondly =
|
||||
CronBuilder.cron(
|
||||
CronDefinitionBuilder.defineCron
|
||||
.withSeconds.and
|
||||
.instance
|
||||
).withSecond(C.every(1)).instance
|
||||
Thread.sleep(
|
||||
ExecutionTime.forCron(cronSecondly)
|
||||
.timeToNextExecution(ZonedDateTime.now)
|
||||
.get.get(ChronoUnit.NANOS)/1000
|
||||
) // aligned current time to millisecond 000
|
||||
note(s"CronTask test time aligned to ${formatDate(currentTimeMillis, 0)}")
|
||||
|
||||
var times = 0
|
||||
val task = CronTask("cron-task", cronSecondly, ZoneOffset.ofHours(0).normalized, {
|
||||
times = times + 1
|
||||
note(s"CronTask executed at ${formatDate(currentTimeMillis, 0)}")
|
||||
})
|
||||
scheduler ++ task
|
||||
Thread.sleep(10300)
|
||||
|
||||
// it should be at 300ms position to 10 seconds
|
||||
|
||||
scheduler % task
|
||||
scheduler.stop()
|
||||
note(s"CronTasks done at ${formatDate(currentTimeMillis, 0)}")
|
||||
times shouldEqual 10
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,7 @@ class IntervalsTest extends MornyTests {
|
||||
val timeUsed = System.currentTimeMillis() - startTime
|
||||
times shouldEqual 10
|
||||
timeUsed should (be <= 2100L and be >= 1900L)
|
||||
info(s"interval 200ms for 10 times used time ${timeUsed}ms")
|
||||
info(s"Interval Task with interval 200ms for 10 times used time ${timeUsed}ms")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,13 +12,15 @@ class SchedulerTest extends MornyTests {
|
||||
|
||||
"Task with scheduleTime smaller than current time should be executed immediately" in {
|
||||
val scheduler = Scheduler()
|
||||
var time = System.currentTimeMillis
|
||||
val time = System.currentTimeMillis
|
||||
var doneTime: Option[Long] = None
|
||||
scheduler ++ Task("task", 0, {
|
||||
time = System.currentTimeMillis - time
|
||||
doneTime = Some(System.currentTimeMillis)
|
||||
})
|
||||
scheduler.waitForStopAtAllDone()
|
||||
time should be <= 10L
|
||||
info(s"Immediately Task done with time $time")
|
||||
Thread.sleep(10)
|
||||
scheduler.stop()
|
||||
doneTime shouldBe defined
|
||||
info(s"Immediately Task done in ${doneTime.get - time}ms")
|
||||
}
|
||||
|
||||
"Task's running thread name should be task name" in {
|
||||
|
Loading…
Reference in New Issue
Block a user