|
|
|
@@ -0,0 +1,148 @@
|
|
|
|
|
# encoding: UTF-8
|
|
|
|
|
|
|
|
|
|
require_relative "test_helper"
|
|
|
|
|
require_relative "../lib/meow/automation"
|
|
|
|
|
|
|
|
|
|
module MeowAutomationTest
|
|
|
|
|
Automation = MeowClient::Automation
|
|
|
|
|
|
|
|
|
|
class FakeProgram
|
|
|
|
|
def initialize
|
|
|
|
|
@documents = {}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def read_json(name, default:)
|
|
|
|
|
Marshal.load(Marshal.dump(@documents.fetch(name, default)))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def update_json(name, default:)
|
|
|
|
|
value = Marshal.load(Marshal.dump(@documents.fetch(name, default)))
|
|
|
|
|
yield value
|
|
|
|
|
@documents[name] = value
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Automation templates expand aliases and regular expression captures") do
|
|
|
|
|
MeowTest.assert_equal("cast fire goblin $", Automation::Template.alias_expand("cast $1 $2 $$", "fire goblin"))
|
|
|
|
|
match = /(red) (dragon)/.match("red dragon")
|
|
|
|
|
MeowTest.assert_equal("dragon/red dragon/$", Automation::Template.match_expand("$2/$0/$$", match))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Automation repository round trips normalized profile rules") do
|
|
|
|
|
repository = Automation::Repository.new(FakeProgram.new)
|
|
|
|
|
configuration = repository.default_configuration
|
|
|
|
|
configuration["aliases"] << {"name" => "Heal", "key" => "h", "command" => "cast heal $*", "enabled" => true}
|
|
|
|
|
configuration["shortcuts"] << {"name" => "Look", "digit" => 1, "shift" => false, "command" => "look", "enabled" => true}
|
|
|
|
|
saved = repository.save("profile", configuration)
|
|
|
|
|
loaded = repository.load("profile")
|
|
|
|
|
MeowTest.assert_equal(saved, loaded)
|
|
|
|
|
MeowTest.assert(saved["aliases"][0]["id"].to_s != "")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Automation repository rejects duplicate aliases and shortcuts") do
|
|
|
|
|
repository = Automation::Repository.new(FakeProgram.new)
|
|
|
|
|
configuration = repository.default_configuration
|
|
|
|
|
configuration["aliases"] = [
|
|
|
|
|
{"name" => "One", "key" => "x", "command" => "one"},
|
|
|
|
|
{"name" => "Two", "key" => "X", "command" => "two"}
|
|
|
|
|
]
|
|
|
|
|
MeowTest.assert_raises(ArgumentError) { repository.save("profile", configuration) }
|
|
|
|
|
|
|
|
|
|
configuration = repository.default_configuration
|
|
|
|
|
configuration["shortcuts"] = [
|
|
|
|
|
{"name" => "One", "digit" => 1, "shift" => false, "command" => "one"},
|
|
|
|
|
{"name" => "Two", "digit" => 1, "shift" => false, "command" => "two"}
|
|
|
|
|
]
|
|
|
|
|
MeowTest.assert_raises(ArgumentError) { repository.save("profile", configuration) }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Command processor expands only one exact first-word alias") do
|
|
|
|
|
processor = Automation::CommandProcessor.new([
|
|
|
|
|
{"key" => "k", "command" => "kill $1", "enabled" => true},
|
|
|
|
|
{"key" => "disabled", "command" => "say no", "enabled" => false}
|
|
|
|
|
])
|
|
|
|
|
MeowTest.assert_equal("kill orc", processor.process("K orc"))
|
|
|
|
|
MeowTest.assert_equal("kk orc", processor.process("kk orc"))
|
|
|
|
|
MeowTest.assert_equal("k orc", processor.process("k orc", :expand_aliases => false))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Output processor applies ordered actions against original input") do
|
|
|
|
|
spoken = []
|
|
|
|
|
sent = []
|
|
|
|
|
media = []
|
|
|
|
|
triggers = [
|
|
|
|
|
{
|
|
|
|
|
"enabled" => true,
|
|
|
|
|
"pattern" => "HP: (\\d+)",
|
|
|
|
|
"case_sensitive" => false,
|
|
|
|
|
"actions" => [
|
|
|
|
|
{"type" => "rewrite", "text" => "Health $1"},
|
|
|
|
|
{"type" => "highlight", "label" => "Warning", "source" => "tone.wav"},
|
|
|
|
|
{"type" => "speak", "text" => "Health is $1"},
|
|
|
|
|
{"type" => "send", "text" => "score"}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
processor = Automation::OutputProcessor.new(
|
|
|
|
|
triggers,
|
|
|
|
|
{
|
|
|
|
|
:speak => proc { |text| spoken << text },
|
|
|
|
|
:send => proc { |text| sent << text },
|
|
|
|
|
:play_media => proc { |source, _action| media << source }
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
result = processor.process("hp: 12")
|
|
|
|
|
MeowTest.assert_equal("Warning: Health 12", result.text)
|
|
|
|
|
MeowTest.refute(result.gagged)
|
|
|
|
|
MeowTest.assert_equal(["Health is 12"], spoken)
|
|
|
|
|
MeowTest.assert_equal(["score"], sent)
|
|
|
|
|
MeowTest.assert_equal(["tone.wav"], media)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Gag suppresses output while preserving side effects") do
|
|
|
|
|
spoken = []
|
|
|
|
|
processor = Automation::OutputProcessor.new(
|
|
|
|
|
[{"enabled" => true, "pattern" => "secret", "actions" => [{"type" => "gag"}, {"type" => "speak", "text" => "noticed"}]}],
|
|
|
|
|
{:speak => proc { |text| spoken << text }}
|
|
|
|
|
)
|
|
|
|
|
result = processor.process("secret")
|
|
|
|
|
MeowTest.assert(result.gagged)
|
|
|
|
|
MeowTest.assert_equal(["noticed"], spoken)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Automation send limiter suspends a runaway rule") do
|
|
|
|
|
now = 10.0
|
|
|
|
|
sent = []
|
|
|
|
|
suspended = 0
|
|
|
|
|
processor = Automation::OutputProcessor.new(
|
|
|
|
|
[],
|
|
|
|
|
{:send => proc { |text| sent << text }, :suspended => proc { suspended += 1 }},
|
|
|
|
|
:clock => proc { now }
|
|
|
|
|
)
|
|
|
|
|
actions = [{"type" => "send", "text" => "look"}]
|
|
|
|
|
(Automation::MAX_AUTOMATED_SENDS_PER_SECOND + 1).times { processor.execute_actions(actions) }
|
|
|
|
|
MeowTest.assert_equal(Automation::MAX_AUTOMATED_SENDS_PER_SECOND, sent.size)
|
|
|
|
|
MeowTest.assert_equal(1, suspended)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Timer scheduler starts at session time and skips missed bursts") do
|
|
|
|
|
now = 100.0
|
|
|
|
|
scheduler = Automation::TimerScheduler.new(
|
|
|
|
|
[{"name" => "pulse", "enabled" => true, "interval" => 5.0, "actions" => []}],
|
|
|
|
|
:clock => proc { now }
|
|
|
|
|
)
|
|
|
|
|
MeowTest.assert_empty(scheduler.due)
|
|
|
|
|
now = 105.0
|
|
|
|
|
MeowTest.assert_equal(["pulse"], scheduler.due.map { |row| row["name"] })
|
|
|
|
|
now = 130.0
|
|
|
|
|
MeowTest.assert_equal(["pulse"], scheduler.due.map { |row| row["name"] })
|
|
|
|
|
MeowTest.assert_empty(scheduler.due)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
MeowTest.test("Automation media distinguishes Windows files from secure URLs") do
|
|
|
|
|
MeowTest.assert_equal(:local, Automation::MediaSource.classify("C:\\sounds\\alert.ogg"))
|
|
|
|
|
MeowTest.assert_equal(:local, Automation::MediaSource.classify("\\\\server\\sounds\\alert.wav"))
|
|
|
|
|
MeowTest.assert_equal(:https, Automation::MediaSource.classify("https://media.example/alert.mp3"))
|
|
|
|
|
MeowTest.assert_raises(ArgumentError) { Automation::MediaSource.classify("http://media.example/alert.mp3") }
|
|
|
|
|
end
|
|
|
|
|
end
|