local G=require("\109\111\115\101\115")local z=require("\105\110\118\111\107\097\116\105\111\110\046\099\111\109\098\111\115\046\067\111\109\098\111\083\116\101\112")local f=require("\105\110\118\111\107\097\116\105\111\110\046\099\111\109\098\111\115\046\067\111\109\098\111\083\101\113\117\101\110\099\101")local u=f.STATES describe("\067\111\109\098\111\083\101\113\117\101\110\099\101",function()local p={{id=1,name="\115\116\101\112\049";required=true,next={2,3}};{id=2;name="\115\116\101\112\050";next={3}},{id=3,name="\115\116\101\112\051";required=true}}local q local function c()q=q+1 return q end local w=G.property("\105\100")local function g(z)return G.map(z,w)end local Q,T,j before_each(function()q=0 T=G.uniqueId()j=spy.new(c)Q=f(T,p,{clock=j})end)describe("\099\111\110\115\116\114\117\099\116\111\114",function()it("\105\110\105\116\105\097\108\105\122\101\115 \097\116\116\114\105\098\117\116\101\115",function()assert.equal(T,Q.id)assert.equal(j,Q.clock)assert.is_table(Q.sequence)assert.is_true(G.all(Q.sequence,function(G)return z:class_of(G)end))end)end)describe("\035\069\110\116\101\114\084\105\109\101",function()it("\114\101\116\117\114\110\115 \116\104\101 \116\105\109\101 \119\104\101\110 \116\104\101 \115\101\113\117\101\110\099\101 \101\110\116\101\114\101\100 \097 \115\116\097\116\101",function()assert.is_true(Q:Progress("\115\116\101\112\049"))assert.is_true(Q:Progress("\115\116\101\112\050"))assert.is_true(Q:Progress("\115\116\101\112\051"))assert.is_true(Q:PreFinish())assert.equal(4,Q:EnterTime(u.PRE_FINISH))assert.is_nil(Q:EnterTime(u.FINISH))assert.spy(Q.clock)end)it("\114\101\116\117\114\110\115 \110\105\108 \102\111\114 \097\110 \105\110\118\097\108\105\100 \115\116\097\116\101",function()assert.is_true(Q:Progress("\115\116\101\112\049"))assert.is_true(Q:Progress("\115\116\101\112\050"))assert.is_true(Q:Progress("\115\116\101\112\051"))assert.is_true(Q:PreFinish())assert.is_true(Q:Finish())assert.is_nil(Q:EnterTime("\105\110\118\097\108\105\100"))end)end)describe("\035\076\101\097\118\101\084\105\109\101",function()it("\114\101\116\117\114\110\115 \116\104\101 \116\105\109\101 \119\104\101\110 \116\104\101 \115\101\113\117\101\110\099\101 \108\101\102\116 \097 \115\116\097\116\101",function()assert.is_true(Q:Progress("\115\116\101\112\049"))assert.is_true(Q:Progress("\115\116\101\112\050"))assert.is_true(Q:Progress("\115\116\101\112\051"))assert.is_true(Q:PreFinish())assert.is_true(Q:Finish())assert.equal(5,Q:LeaveTime(u.PRE_FINISH))assert.is_nil(Q:LeaveTime(u.FINISH))end)it("\114\101\116\117\114\110\115 \110\105\108 \102\111\114 \097\110 \105\110\118\097\108\105\100 \115\116\097\116\101",function()assert.is_true(Q:Progress("\115\116\101\112\049"))assert.is_true(Q:Progress("\115\116\101\112\050"))assert.is_true(Q:Progress("\115\116\101\112\051"))assert.is_true(Q:PreFinish())assert.is_true(Q:Finish())assert.is_nil(Q:LeaveTime("\105\110\118\097\108\105\100"))end)end)describe("\035\080\114\111\103\114\101\115\115",function()describe("\119\105\116\104 \097 \118\097\108\105\100 \101\118\101\110\116",function()it("\112\114\111\103\114\101\115\115\101\115 \116\104\101 \115\101\113\117\101\110\099\101",function()assert.is_nil(Q.current)assert.is_nil(Q.currentId)assert.same({1},Q.nextIds)assert.same({1},g(Q.next))assert.is_nil(Q:EnterTime(u.INITIAL))assert.is_nil(Q:LeaveTime(u.INITIAL))assert.is_true(Q:Progress("\115\116\101\112\049"))assert.same(1,Q.current.id)assert.equal(1,Q.currentId)assert.same({2,3},Q.nextIds)assert.same({2,3},g(Q.next))assert.equal(1,Q:LeaveTime(u.INITIAL))assert.is_true(Q:Progress("\115\116\101\112\050"))assert.same(2,Q.current.id)assert.equal(2,Q.currentId)assert.same({3},Q.nextIds)assert.same({3},g(Q.next))assert.is_true(Q:Progress("\115\116\101\112\051"))assert.same(3,Q.current.id)assert.equal(3,Q.currentId)assert.same({},Q.nextIds)assert.same({},g(Q.next))assert.is_true(Q:PreFinish())assert.is_nil(Q.current)assert.is_nil(Q.currentId)assert.same({},Q.nextIds)assert.same({},g(Q.next))assert.equal(4,Q:EnterTime(u.PRE_FINISH))assert.is_true(Q:Finish())assert.is_nil(Q.current)assert.is_nil(Q.currentId)assert.same({},Q.nextIds)assert.same({},g(Q.next))assert.equal(5,Q:LeaveTime(u.PRE_FINISH))assert.equal(5,Q:EnterTime(u.FINISH))end)it("\101\110\097\098\108\101\115 \115\107\105\112\112\105\110\103 \111\112\116\105\111\110\097\108 \115\116\101\112\115",function()assert.is_nil(Q.current)assert.is_nil(Q.currentId)assert.same({1},Q.nextIds)assert.same({1},g(Q.next))assert.is_nil(Q:EnterTime(u.INITIAL))assert.is_nil(Q:LeaveTime(u.INITIAL))assert.is_true(Q:Progress("\115\116\101\112\049"))assert.same(1,Q.current.id)assert.equal(1,Q.currentId)assert.same({2;3},Q.nextIds)assert.same({2;3},g(Q.next))assert.equal(1,Q:LeaveTime(u.INITIAL))assert.is_true(Q:Progress("\115\116\101\112\051"))assert.same(3,Q.current.id)assert.equal(3,Q.currentId)assert.same({},Q.nextIds)assert.same({},g(Q.next))assert.is_true(Q:PreFinish())assert.is_nil(Q.current)assert.is_nil(Q.currentId)assert.same({},Q.nextIds)assert.same({},g(Q.next))assert.equal(3,Q:EnterTime(u.PRE_FINISH))assert.is_true(Q:Finish())assert.is_nil(Q.current)assert.is_nil(Q.currentId)assert.same({},Q.nextIds)assert.same({},g(Q.next))assert.equal(4,Q:LeaveTime(u.PRE_FINISH))assert.equal(4,Q:EnterTime(u.FINISH))end)end)describe("\119\105\116\104 \097\110 \105\110\118\097\108\105\100 \101\118\101\110\116",function()it("\100\111\101\115 \078\079\084 \112\114\111\103\114\101\115\115 \116\104\101 \115\101\113\117\101\110\099\101",function()assert.is_false(Q:Progress("\105\110\118\097\108\105\100"))assert.is_false(Q:Progress("\115\116\101\112\050"))assert.is_true(Q:Progress("\115\116\101\112\049"))assert.is_true(Q:Progress("\115\116\101\112\051"))assert.is_false(Q:Progress("\115\116\101\112\049"))assert.is_false(Q:Progress("\115\116\101\112\050"))assert.is_false(Q:Progress("\105\110\118\097\108\105\100"))end)end)end)describe("\035\080\114\101\070\105\110\105\115\104",function()it("\112\114\101-\102\105\110\105\115\104\101\115 \116\104\101 \115\101\113\117\101\110\099\101",function()assert.is_false(Q:PreFinish())assert.is_true(Q:Progress("\115\116\101\112\049"))assert.is_false(Q:PreFinish())assert.is_true(Q:Progress("\115\116\101\112\050"))assert.is_false(Q:PreFinish())assert.is_true(Q:Progress("\115\116\101\112\051"))assert.is_true(Q:PreFinish())end)end)describe("\035\070\105\110\105\115\104",function()it("\102\105\110\105\115\104\101\115 \116\104\101 \115\101\113\117\101\110\099\101",function()assert.is_false(Q:Finish())assert.is_true(Q:Progress("\115\116\101\112\049"))assert.is_false(Q:Finish())assert.is_true(Q:Progress("\115\116\101\112\050"))assert.is_false(Q:Finish())assert.is_true(Q:Progress("\115\116\101\112\051"))assert.is_false(Q:Finish())assert.is_true(Q:PreFinish())assert.is_true(Q:Finish())end)it("\115\101\116\115 \116\104\101 \100\117\114\097\116\105\111\110",function()assert.is_true(Q:Progress("\115\116\101\112\049"))assert.is_true(Q:Progress("\115\116\101\112\050"))assert.is_true(Q:Progress("\115\116\101\112\051"))assert.is_true(Q:PreFinish())assert.is_true(Q:Finish())assert.equal(4,Q.duration)end)end)end)
local m = require("moses")

local ComboStep = require("invokation.combos.ComboStep")
local ComboSequence = require("invokation.combos.ComboSequence")

local STATES = ComboSequence.STATES

describe("ComboSequence", function()
  local steps = {
    { id = 1, name = "step1", required = true, next = { 2, 3 } },
    { id = 2, name = "step2", next = { 3 } },
    { id = 3, name = "step3", required = true },
  }

  local clockIdx
  local function clock()
    clockIdx = clockIdx + 1
    return clockIdx
  end

  local getId = m.property("id")
  local function stepsIds(arr)
    return m.map(arr, getId)
  end

  local seq, seqId, spyClock

  before_each(function()
    clockIdx = 0
    seqId = m.uniqueId()
    spyClock = spy.new(clock)
    seq = ComboSequence(seqId, steps, { clock = spyClock })
  end)

  describe("constructor", function()
    it("initializes attributes", function()
      assert.equal(seqId, seq.id)
      assert.equal(spyClock, seq.clock)
      assert.is_table(seq.sequence)
      assert.is_true(m.all(seq.sequence, function(s)
        return ComboStep:class_of(s)
      end))
    end)
  end)

  describe("#EnterTime", function()
    it("returns the time when the sequence entered a state", function()
      assert.is_true(seq:Progress("step1"))
      assert.is_true(seq:Progress("step2"))
      assert.is_true(seq:Progress("step3"))
      assert.is_true(seq:PreFinish())
      assert.equal(4, seq:EnterTime(STATES.PRE_FINISH))
      assert.is_nil(seq:EnterTime(STATES.FINISH))
      assert.spy(seq.clock)
    end)

    it("returns nil for an invalid state", function()
      assert.is_true(seq:Progress("step1"))
      assert.is_true(seq:Progress("step2"))
      assert.is_true(seq:Progress("step3"))
      assert.is_true(seq:PreFinish())
      assert.is_true(seq:Finish())
      assert.is_nil(seq:EnterTime("invalid"))
    end)
  end)

  describe("#LeaveTime", function()
    it("returns the time when the sequence left a state", function()
      assert.is_true(seq:Progress("step1"))
      assert.is_true(seq:Progress("step2"))
      assert.is_true(seq:Progress("step3"))
      assert.is_true(seq:PreFinish())
      assert.is_true(seq:Finish())
      assert.equal(5, seq:LeaveTime(STATES.PRE_FINISH))
      assert.is_nil(seq:LeaveTime(STATES.FINISH))
    end)

    it("returns nil for an invalid state", function()
      assert.is_true(seq:Progress("step1"))
      assert.is_true(seq:Progress("step2"))
      assert.is_true(seq:Progress("step3"))
      assert.is_true(seq:PreFinish())
      assert.is_true(seq:Finish())
      assert.is_nil(seq:LeaveTime("invalid"))
    end)
  end)

  describe("#Progress", function()
    describe("with a valid event", function()
      it("progresses the sequence", function()
        assert.is_nil(seq.current)
        assert.is_nil(seq.currentId)
        assert.same({ 1 }, seq.nextIds)
        assert.same({ 1 }, stepsIds(seq.next))
        assert.is_nil(seq:EnterTime(STATES.INITIAL))
        assert.is_nil(seq:LeaveTime(STATES.INITIAL))

        assert.is_true(seq:Progress("step1"))

        assert.same(1, seq.current.id)
        assert.equal(1, seq.currentId)
        assert.same({ 2, 3 }, seq.nextIds)
        assert.same({ 2, 3 }, stepsIds(seq.next))
        assert.equal(1, seq:LeaveTime(STATES.INITIAL))

        assert.is_true(seq:Progress("step2"))

        assert.same(2, seq.current.id)
        assert.equal(2, seq.currentId)
        assert.same({ 3 }, seq.nextIds)
        assert.same({ 3 }, stepsIds(seq.next))

        assert.is_true(seq:Progress("step3"))

        assert.same(3, seq.current.id)
        assert.equal(3, seq.currentId)
        assert.same({}, seq.nextIds)
        assert.same({}, stepsIds(seq.next))

        assert.is_true(seq:PreFinish())

        assert.is_nil(seq.current)
        assert.is_nil(seq.currentId)
        assert.same({}, seq.nextIds)
        assert.same({}, stepsIds(seq.next))
        assert.equal(4, seq:EnterTime(STATES.PRE_FINISH))

        assert.is_true(seq:Finish())

        assert.is_nil(seq.current)
        assert.is_nil(seq.currentId)
        assert.same({}, seq.nextIds)
        assert.same({}, stepsIds(seq.next))
        assert.equal(5, seq:LeaveTime(STATES.PRE_FINISH))
        assert.equal(5, seq:EnterTime(STATES.FINISH))
      end)

      it("enables skipping optional steps", function()
        assert.is_nil(seq.current)
        assert.is_nil(seq.currentId)
        assert.same({ 1 }, seq.nextIds)
        assert.same({ 1 }, stepsIds(seq.next))
        assert.is_nil(seq:EnterTime(STATES.INITIAL))
        assert.is_nil(seq:LeaveTime(STATES.INITIAL))

        assert.is_true(seq:Progress("step1"))

        assert.same(1, seq.current.id)
        assert.equal(1, seq.currentId)
        assert.same({ 2, 3 }, seq.nextIds)
        assert.same({ 2, 3 }, stepsIds(seq.next))
        assert.equal(1, seq:LeaveTime(STATES.INITIAL))

        assert.is_true(seq:Progress("step3"))

        assert.same(3, seq.current.id)
        assert.equal(3, seq.currentId)
        assert.same({}, seq.nextIds)
        assert.same({}, stepsIds(seq.next))

        assert.is_true(seq:PreFinish())

        assert.is_nil(seq.current)
        assert.is_nil(seq.currentId)
        assert.same({}, seq.nextIds)
        assert.same({}, stepsIds(seq.next))
        assert.equal(3, seq:EnterTime(STATES.PRE_FINISH))

        assert.is_true(seq:Finish())

        assert.is_nil(seq.current)
        assert.is_nil(seq.currentId)
        assert.same({}, seq.nextIds)
        assert.same({}, stepsIds(seq.next))
        assert.equal(4, seq:LeaveTime(STATES.PRE_FINISH))
        assert.equal(4, seq:EnterTime(STATES.FINISH))
      end)
    end)

    describe("with an invalid event", function()
      it("does NOT progress the sequence", function()
        assert.is_false(seq:Progress("invalid"))
        assert.is_false(seq:Progress("step2"))
        assert.is_true(seq:Progress("step1"))
        assert.is_true(seq:Progress("step3"))
        assert.is_false(seq:Progress("step1"))
        assert.is_false(seq:Progress("step2"))
        assert.is_false(seq:Progress("invalid"))
      end)
    end)
  end)

  describe("#PreFinish", function()
    it("pre-finishes the sequence", function()
      assert.is_false(seq:PreFinish())
      assert.is_true(seq:Progress("step1"))
      assert.is_false(seq:PreFinish())
      assert.is_true(seq:Progress("step2"))
      assert.is_false(seq:PreFinish())
      assert.is_true(seq:Progress("step3"))
      assert.is_true(seq:PreFinish())
    end)
  end)

  describe("#Finish", function()
    it("finishes the sequence", function()
      assert.is_false(seq:Finish())
      assert.is_true(seq:Progress("step1"))
      assert.is_false(seq:Finish())
      assert.is_true(seq:Progress("step2"))
      assert.is_false(seq:Finish())
      assert.is_true(seq:Progress("step3"))
      assert.is_false(seq:Finish())
      assert.is_true(seq:PreFinish())
      assert.is_true(seq:Finish())
    end)

    it("sets the duration", function()
      assert.is_true(seq:Progress("step1"))
      assert.is_true(seq:Progress("step2"))
      assert.is_true(seq:Progress("step3"))
      assert.is_true(seq:PreFinish())
      assert.is_true(seq:Finish())
      assert.equal(4, seq.duration)
    end)
  end)
end)
