/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
"testing"
. "git.tizen.org/tools/boruta"
+ "github.com/stretchr/testify/assert"
)
func TestRemovePanic(t *testing.T) {
- assert, rqueue := initTest(t)
- assert.Panics(func() { rqueue.queue._remove(ReqID(1), LoPrio) })
+ assert := assert.New(t)
+ queue := newPrioQueue()
+ assert.Panics(func() { queue._remove(ReqID(1), LoPrio) })
}
func TestQueue(t *testing.T) {
- assert, rqueue := initTest(t)
+ assert := assert.New(t)
+ queue := newPrioQueue()
var reqs = []struct {
id ReqID
pr Priority
sorted := []ReqID{ReqID(2), ReqID(3), ReqID(5), ReqID(6), ReqID(1), ReqID(4)}
// Test for empty queue.
- reqid, ok := rqueue.queue.next()
+ reqid, ok := queue.next()
assert.False(ok)
assert.Equal(ReqID(0), reqid)
// Test if iterator was initialized and queue is empty.
- rqueue.queue.initIterator()
- reqid, ok = rqueue.queue.next()
+ queue.initIterator()
+ reqid, ok = queue.next()
assert.False(ok)
assert.Equal(ReqID(0), reqid)
- rqueue.queue.releaseIterator()
+ queue.releaseIterator()
req := requestsTests[0].req
// Push requests to the queue.
for _, r := range reqs {
- _, err := rqueue.NewRequest(req.Caps, r.pr, req.Owner, req.ValidAfter, req.Deadline)
- assert.Nil(err)
+ queue.pushRequest(&ReqInfo{
+ ID: r.id,
+ Priority: r.pr,
+ Owner: req.Owner,
+ Deadline: req.Deadline,
+ ValidAfter: req.ValidAfter,
+ State: WAIT,
+ Caps: req.Caps,
+ })
}
// Check if queue returns request IDs in proper order.
- rqueue.queue.initIterator()
+ queue.initIterator()
for _, r := range sorted {
- reqid, ok = rqueue.queue.next()
+ reqid, ok = queue.next()
assert.True(ok)
assert.Equal(r, reqid)
}
// Check if call to next() after iterating through whole queue returns false.
- reqid, ok = rqueue.queue.next()
+ reqid, ok = queue.next()
assert.False(ok)
assert.Equal(ReqID(0), reqid)
- rqueue.queue.releaseIterator()
+ queue.releaseIterator()
// Check if after another initialization next() returns first element.
- rqueue.queue.initIterator()
- reqid, ok = rqueue.queue.next()
+ queue.initIterator()
+ reqid, ok = queue.next()
assert.True(ok)
assert.Equal(sorted[0], reqid)
// Check call to releaseIterator() when iterator hasn't finished properly
// sets next().
- rqueue.queue.releaseIterator()
- reqid, ok = rqueue.queue.next()
+ queue.releaseIterator()
+ reqid, ok = queue.next()
assert.False(ok)
assert.Equal(ReqID(0), reqid)
}
"time"
. "git.tizen.org/tools/boruta"
+ "git.tizen.org/tools/boruta/matcher"
)
// ReqsCollection contains information (also historical) about handled requests.
// It implements Requests and RequestsManager interfaces.
type ReqsCollection struct {
- requests map[ReqID]*ReqInfo
- queue *prioQueue
- mutex *sync.RWMutex
- iterating bool
+ requests map[ReqID]*ReqInfo
+ queue *prioQueue
+ mutex *sync.RWMutex
+ iterating bool
+ workers matcher.WorkersManager
+ jobs matcher.JobsManager
+ validAfterTimes *requestTimes
+ deadlineTimes *requestTimes
+ timeoutTimes *requestTimes
+ validAfterMatcher matcher.Matcher
+ deadlineMatcher matcher.Matcher
+ timeoutMatcher matcher.Matcher
}
// NewRequestQueue provides initialized priority queue for requests.
-func NewRequestQueue() *ReqsCollection {
- return &ReqsCollection{
- requests: make(map[ReqID]*ReqInfo),
- queue: newPrioQueue(),
- mutex: new(sync.RWMutex),
- }
+func NewRequestQueue(w matcher.WorkersManager, j matcher.JobsManager) *ReqsCollection {
+ r := &ReqsCollection{
+ requests: make(map[ReqID]*ReqInfo),
+ queue: newPrioQueue(),
+ mutex: new(sync.RWMutex),
+ workers: w,
+ jobs: j,
+ validAfterTimes: newRequestTimes(),
+ deadlineTimes: newRequestTimes(),
+ timeoutTimes: newRequestTimes(),
+ }
+
+ r.validAfterMatcher = matcher.NewValidMatcher(r, w, j)
+ r.deadlineMatcher = matcher.NewDeadlineMatcher(r)
+ r.timeoutMatcher = matcher.NewTimeoutMatcher(r)
+
+ r.validAfterTimes.setMatcher(r.validAfterMatcher)
+ r.deadlineTimes.setMatcher(r.deadlineMatcher)
+ r.timeoutTimes.setMatcher(r.timeoutMatcher)
+
+ return r
+}
+
+// Finish releases requestTimes queues and stops started goroutines.
+func (reqs *ReqsCollection) Finish() {
+ reqs.validAfterTimes.finish()
+ reqs.deadlineTimes.finish()
+ reqs.timeoutTimes.finish()
}
// NewRequest is part of implementation of Requests interface. It validates
reqs.requests[req.ID] = req
reqs.mutex.Unlock()
+ reqs.validAfterTimes.insert(requestTime{time: req.ValidAfter, req: req.ID})
+ reqs.deadlineTimes.insert(requestTime{time: req.Deadline, req: req.ID})
+
return req.ID, nil
}
src.Deadline.IsZero()) {
return nil
}
+ validAfterTime, deadlineTime, err := reqs.updateRequest(src)
+ if err != nil {
+ return err
+ }
+ if validAfterTime != nil {
+ reqs.validAfterTimes.insert(*validAfterTime)
+ }
+ if deadlineTime != nil {
+ reqs.deadlineTimes.insert(*deadlineTime)
+ }
+ return nil
+}
+
+// updateRequest is a part of UpdateRequest implementation run in critical section.
+func (reqs *ReqsCollection) updateRequest(src *ReqInfo) (validAfterTime, deadlineTime *requestTime, err error) {
reqs.mutex.Lock()
defer reqs.mutex.Unlock()
dst, ok := reqs.requests[src.ID]
if !ok {
- return NotFoundError("Request")
+ err = NotFoundError("Request")
+ return
}
if !modificationPossible(dst.State) {
- return ErrModificationForbidden
+ err = ErrModificationForbidden
+ return
}
if src.Priority == dst.Priority &&
src.ValidAfter.Equal(dst.ValidAfter) &&
src.Deadline.Equal(dst.Deadline) {
- return nil
+ return
}
// TODO(mwereski): Check if user has rights to set given priority.
if src.Priority != Priority(0) && (src.Priority < HiPrio ||
src.Priority > LoPrio) {
- return ErrPriority
+ err = ErrPriority
+ return
}
deadline := dst.Deadline
if !src.Deadline.IsZero() {
if src.Deadline.Before(time.Now().UTC()) {
- return ErrDeadlineInThePast
+ err = ErrDeadlineInThePast
+ return
}
deadline = src.Deadline
}
if (!src.ValidAfter.IsZero()) && !deadline.IsZero() &&
src.ValidAfter.After(deadline) {
- return ErrInvalidTimeRange
+ err = ErrInvalidTimeRange
+ return
}
if src.Priority != Priority(0) {
}
if !src.ValidAfter.IsZero() {
dst.ValidAfter = src.ValidAfter
+ validAfterTime = &requestTime{time: src.ValidAfter, req: src.ID}
}
- dst.Deadline = deadline
- // TODO(mwereski): check if request is ready to go.
- return nil
+ if !dst.Deadline.Equal(deadline) {
+ dst.Deadline = deadline
+ deadlineTime = &requestTime{time: deadline, req: src.ID}
+ }
+ return
}
// GetRequestInfo is part of implementation of Requests interface. It returns
package requests
import (
+ "errors"
"time"
. "git.tizen.org/tools/boruta"
+ gomock "github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Requests as RequestsManager", func() {
- var R *ReqsCollection
- BeforeEach(func() {
- R = NewRequestQueue()
- })
- Describe("Iterations", func() {
- var entered chan int
- testMutex := func() {
- R.mutex.Lock()
- defer R.mutex.Unlock()
- entered <- 1
- }
+ Describe("With RequestsManager created", func() {
+ var ctrl *gomock.Controller
+ var wm *MockWorkersManager
+ var R *ReqsCollection
+ testErr := errors.New("Test Error")
+
BeforeEach(func() {
- entered = make(chan int)
+ ctrl = gomock.NewController(GinkgoT())
+ wm = NewMockWorkersManager(ctrl)
+ R = NewRequestQueue(wm, nil)
+ })
+ AfterEach(func() {
+ R.Finish()
+ ctrl.Finish()
})
- Describe("InitIteration", func() {
- It("should init iterations and lock requests mutex", func() {
- err := R.InitIteration()
- Expect(err).NotTo(HaveOccurred())
- Expect(R.iterating).To(BeTrue())
-
- // Verify that mutex is locked.
- go testMutex()
- Consistently(entered).ShouldNot(Receive())
- // Release the mutex
- R.mutex.Unlock()
- Eventually(entered).Should(Receive())
- })
- It("should return error and remain mutex unlocked if iterations are already started", func() {
+ Describe("Iterations", func() {
+ var entered chan int
+ testMutex := func() {
R.mutex.Lock()
- R.iterating = true
- R.mutex.Unlock()
+ defer R.mutex.Unlock()
+ entered <- 1
+ }
+ BeforeEach(func() {
+ entered = make(chan int)
+ })
+ Describe("InitIteration", func() {
+ It("should init iterations and lock requests mutex", func() {
+ err := R.InitIteration()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(R.iterating).To(BeTrue())
- err := R.InitIteration()
- Expect(err).To(Equal(ErrInternalLogicError))
+ // Verify that mutex is locked.
+ go testMutex()
+ Consistently(entered).ShouldNot(Receive())
- // Verify that mutex is not locked.
- go testMutex()
- Eventually(entered).Should(Receive())
- })
- })
- Describe("TerminateIteration", func() {
- It("should terminate iterations and unlock requests mutex", func() {
- err := R.InitIteration()
- Expect(err).NotTo(HaveOccurred())
+ // Release the mutex
+ R.mutex.Unlock()
+ Eventually(entered).Should(Receive())
+ })
+ It("should return error and remain mutex unlocked if iterations are already started", func() {
+ R.mutex.Lock()
+ R.iterating = true
+ R.mutex.Unlock()
- R.TerminateIteration()
- Expect(R.iterating).To(BeFalse())
+ err := R.InitIteration()
+ Expect(err).To(Equal(ErrInternalLogicError))
- // Verify that mutex is not locked.
- go testMutex()
- Eventually(entered).Should(Receive())
+ // Verify that mutex is not locked.
+ go testMutex()
+ Eventually(entered).Should(Receive())
+ })
})
- It("should just release mutex if iterations are not started", func() {
- R.mutex.Lock()
+ Describe("TerminateIteration", func() {
+ It("should terminate iterations and unlock requests mutex", func() {
+ err := R.InitIteration()
+ Expect(err).NotTo(HaveOccurred())
- R.TerminateIteration()
+ R.TerminateIteration()
+ Expect(R.iterating).To(BeFalse())
- // Verify that mutex is not locked.
- go testMutex()
- Eventually(entered).Should(Receive())
- })
- })
- })
- Describe("Iterating over requests", func() {
- verify := []ReqID{3, 5, 1, 2, 7, 4, 6}
- BeforeEach(func() {
- now := time.Now()
- tomorrow := now.AddDate(0, 0, 1)
- insert := func(p Priority) {
- _, err := R.NewRequest(Capabilities{}, p, UserInfo{}, now, tomorrow)
- Expect(err).NotTo(HaveOccurred())
- }
- insert(3) //1
- insert(3) //2
- insert(1) //3
- insert(5) //4
- insert(1) //5
- insert(5) //6
- insert(3) //7
- })
- It("should properly iterate over requests", func() {
- reqs := make([]ReqID, 0)
+ // Verify that mutex is not locked.
+ go testMutex()
+ Eventually(entered).Should(Receive())
+ })
+ It("should just release mutex if iterations are not started", func() {
+ R.mutex.Lock()
- R.InitIteration()
- for r, ok := R.Next(); ok; r, ok = R.Next() {
- reqs = append(reqs, r)
- }
- R.TerminateIteration()
+ R.TerminateIteration()
- Expect(reqs).To(Equal(verify))
+ // Verify that mutex is not locked.
+ go testMutex()
+ Eventually(entered).Should(Receive())
+ })
+ })
})
- It("should restart iterations in new critical section", func() {
- for times := 0; times < len(verify); times++ {
+ Describe("Iterating over requests", func() {
+ verify := []ReqID{3, 5, 1, 2, 7, 4, 6}
+ BeforeEach(func() {
+ now := time.Now()
+ tomorrow := now.AddDate(0, 0, 1)
+ wm.EXPECT().TakeBestMatchingWorker(gomock.Any(), gomock.Any()).Return(WorkerUUID(""), testErr).AnyTimes()
+ insert := func(p Priority) {
+ _, err := R.NewRequest(Capabilities{}, p, UserInfo{}, now, tomorrow)
+ Expect(err).NotTo(HaveOccurred())
+ }
+ insert(3) //1
+ insert(3) //2
+ insert(1) //3
+ insert(5) //4
+ insert(1) //5
+ insert(5) //6
+ insert(3) //7
+ })
+ It("should properly iterate over requests", func() {
reqs := make([]ReqID, 0)
- i := 0
+
R.InitIteration()
- for r, ok := R.Next(); ok && i < times; r, ok = R.Next() {
+ for r, ok := R.Next(); ok; r, ok = R.Next() {
reqs = append(reqs, r)
- i++
}
R.TerminateIteration()
- Expect(reqs).To(Equal(verify[:times]))
- }
- })
- It("should panic if Next is called without InitIteration", func() {
- wrap := func() {
- R.mutex.Lock()
- defer R.mutex.Unlock()
- R.Next()
- }
- Expect(wrap).To(Panic())
- })
- })
- Describe("With request in the queue", func() {
- var now, tomorrow time.Time
- var req, noreq ReqID
- var rinfo *ReqInfo
- BeforeEach(func() {
- now = time.Now()
- tomorrow = now.AddDate(0, 0, 1)
- var err error
- req, err = R.NewRequest(Capabilities{}, 3, UserInfo{}, now, tomorrow)
- Expect(err).NotTo(HaveOccurred())
- var ok bool
- rinfo, ok = R.requests[req]
- Expect(ok).To(BeTrue())
- noreq = req + 1
- })
- Describe("VerifyIfReady", func() {
- It("should fail if reqID is unknown", func() {
- Expect(R.VerifyIfReady(noreq, now)).To(BeFalse())
+
+ Expect(reqs).To(Equal(verify))
})
- It("should fail if state is not WAIT", func() {
- states := []ReqState{INPROGRESS, CANCEL, TIMEOUT, INVALID, DONE, FAILED}
- for _, s := range states {
- rinfo.State = s
- Expect(R.VerifyIfReady(req, now)).To(BeFalse(), "state = %v", s)
+ It("should restart iterations in new critical section", func() {
+ for times := 0; times < len(verify); times++ {
+ reqs := make([]ReqID, 0)
+ i := 0
+ R.InitIteration()
+ for r, ok := R.Next(); ok && i < times; r, ok = R.Next() {
+ reqs = append(reqs, r)
+ i++
+ }
+ R.TerminateIteration()
+ Expect(reqs).To(Equal(verify[:times]))
}
})
- It("should fail if Deadline is reached or passed", func() {
- Expect(R.VerifyIfReady(req, tomorrow.Add(-time.Hour))).To(BeTrue())
- Expect(R.VerifyIfReady(req, tomorrow)).To(BeFalse())
- Expect(R.VerifyIfReady(req, tomorrow.Add(time.Hour))).To(BeFalse())
- })
- It("should fail if ValidAfter is in future", func() {
- Expect(R.VerifyIfReady(req, now.Add(-time.Hour))).To(BeFalse())
- Expect(R.VerifyIfReady(req, now)).To(BeTrue())
- Expect(R.VerifyIfReady(req, now.Add(time.Hour))).To(BeTrue())
- })
- It("should succeed if request is known, in WAIT state and now is between ValidAfter and Deadline", func() {
- Expect(R.VerifyIfReady(req, now.Add(12*time.Hour))).To(BeTrue())
+ It("should panic if Next is called without InitIteration", func() {
+ wrap := func() {
+ R.mutex.Lock()
+ defer R.mutex.Unlock()
+ R.Next()
+ }
+ Expect(wrap).To(Panic())
})
})
- Describe("Get", func() {
- It("should fail if reqID is unknown", func() {
- r, err := R.Get(noreq)
- Expect(err).To(Equal(NotFoundError("Request")))
- Expect(r).To(Equal(ReqInfo{}))
- })
- It("should succeed if reqID is valid", func() {
- r, err := R.Get(req)
+ Describe("With request in the queue", func() {
+ var now, tomorrow time.Time
+ var req, noreq ReqID
+ var rinfo *ReqInfo
+ BeforeEach(func() {
+ now = time.Now()
+ tomorrow = now.AddDate(0, 0, 1)
+ wm.EXPECT().TakeBestMatchingWorker(gomock.Any(), gomock.Any()).Return(WorkerUUID(""), testErr).AnyTimes()
+ var err error
+ req, err = R.NewRequest(Capabilities{}, 3, UserInfo{}, now, tomorrow)
Expect(err).NotTo(HaveOccurred())
- Expect(r).To(Equal(*rinfo))
- })
- })
- Describe("Timeout", func() {
- It("should fail if reqID is unknown", func() {
- Expect(R.queue.length).To(Equal(uint(1)))
- err := R.Timeout(noreq)
- Expect(err).To(Equal(NotFoundError("Request")))
- Expect(R.queue.length).To(Equal(uint(1)))
- })
- It("should fail if request is not in WAIT state", func() {
- rinfo.Deadline = now.Add(-time.Hour)
- Expect(R.queue.length).To(Equal(uint(1)))
- states := []ReqState{INPROGRESS, CANCEL, TIMEOUT, INVALID, DONE, FAILED}
- for _, s := range states {
- rinfo.State = s
- err := R.Timeout(req)
- Expect(err).To(Equal(ErrModificationForbidden), "state = %v", s)
- Expect(R.queue.length).To(Equal(uint(1)), "state = %v", s)
- }
- })
- It("should fail if deadline is in the future", func() {
- Expect(R.queue.length).To(Equal(uint(1)))
- err := R.Timeout(req)
- Expect(err).To(Equal(ErrModificationForbidden))
- Expect(R.queue.length).To(Equal(uint(1)))
+ var ok bool
+ R.mutex.Lock()
+ rinfo, ok = R.requests[req]
+ R.mutex.Unlock()
+ Expect(ok).To(BeTrue())
+ noreq = req + 1
})
- It("should pass if deadline is past", func() {
- rinfo.Deadline = now.Add(-time.Hour)
- Expect(R.queue.length).To(Equal(uint(1)))
- err := R.Timeout(req)
- Expect(err).NotTo(HaveOccurred())
- Expect(rinfo.State).To(Equal(TIMEOUT))
- Expect(R.queue.length).To(BeZero())
+ Describe("VerifyIfReady", func() {
+ It("should fail if reqID is unknown", func() {
+ Expect(R.VerifyIfReady(noreq, now)).To(BeFalse())
+ })
+ It("should fail if state is not WAIT", func() {
+ states := []ReqState{INPROGRESS, CANCEL, TIMEOUT, INVALID, DONE, FAILED}
+ for _, s := range states {
+ R.mutex.Lock()
+ rinfo.State = s
+ R.mutex.Unlock()
+ Expect(R.VerifyIfReady(req, now)).To(BeFalse(), "state = %v", s)
+ }
+ })
+ It("should fail if Deadline is reached or passed", func() {
+ Expect(R.VerifyIfReady(req, tomorrow.Add(-time.Hour))).To(BeTrue())
+ Expect(R.VerifyIfReady(req, tomorrow)).To(BeFalse())
+ Expect(R.VerifyIfReady(req, tomorrow.Add(time.Hour))).To(BeFalse())
+ })
+ It("should fail if ValidAfter is in future", func() {
+ Expect(R.VerifyIfReady(req, now.Add(-time.Hour))).To(BeFalse())
+ Expect(R.VerifyIfReady(req, now)).To(BeTrue())
+ Expect(R.VerifyIfReady(req, now.Add(time.Hour))).To(BeTrue())
+ })
+ It("should succeed if request is known, in WAIT state and now is between ValidAfter and Deadline", func() {
+ Expect(R.VerifyIfReady(req, now.Add(12*time.Hour))).To(BeTrue())
+ })
})
- })
- Describe("Close", func() {
- It("should fail if reqID is unknown", func() {
- err := R.Close(noreq)
- Expect(err).To(Equal(NotFoundError("Request")))
+ Describe("Get", func() {
+ It("should fail if reqID is unknown", func() {
+ r, err := R.Get(noreq)
+ Expect(err).To(Equal(NotFoundError("Request")))
+ Expect(r).To(Equal(ReqInfo{}))
+ })
+ It("should succeed if reqID is valid", func() {
+ r, err := R.Get(req)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(r).To(Equal(*rinfo))
+ })
})
- It("should fail if request is not in INPROGRESS state", func() {
- states := []ReqState{WAIT, CANCEL, TIMEOUT, INVALID, DONE, FAILED}
- for _, state := range states {
+ Describe("Timeout", func() {
+ It("should fail if reqID is unknown", func() {
+ Expect(R.queue.length).To(Equal(uint(1)))
+ err := R.Timeout(noreq)
+ Expect(err).To(Equal(NotFoundError("Request")))
+ Expect(R.queue.length).To(Equal(uint(1)))
+ })
+ It("should fail if request is not in WAIT state", func() {
R.mutex.Lock()
- rinfo.State = state
+ rinfo.Deadline = now.Add(-time.Hour)
R.mutex.Unlock()
-
- err := R.Close(req)
- Expect(err).To(Equal(ErrModificationForbidden), "state = %s", state)
- }
+ Expect(R.queue.length).To(Equal(uint(1)))
+ states := []ReqState{INPROGRESS, CANCEL, TIMEOUT, INVALID, DONE, FAILED}
+ for _, s := range states {
+ R.mutex.Lock()
+ rinfo.State = s
+ R.mutex.Unlock()
+ err := R.Timeout(req)
+ Expect(err).To(Equal(ErrModificationForbidden), "state = %v", s)
+ Expect(R.queue.length).To(Equal(uint(1)), "state = %v", s)
+ }
+ })
+ It("should fail if deadline is in the future", func() {
+ Expect(R.queue.length).To(Equal(uint(1)))
+ err := R.Timeout(req)
+ Expect(err).To(Equal(ErrModificationForbidden))
+ Expect(R.queue.length).To(Equal(uint(1)))
+ })
+ It("should pass if deadline is past", func() {
+ R.mutex.Lock()
+ rinfo.Deadline = now.Add(-time.Hour)
+ R.mutex.Unlock()
+ Expect(R.queue.length).To(Equal(uint(1)))
+ err := R.Timeout(req)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(rinfo.State).To(Equal(TIMEOUT))
+ Expect(R.queue.length).To(BeZero())
+ })
})
- It("should fail if request has no job assigned", func() {
- R.mutex.Lock()
- rinfo.State = INPROGRESS
- Expect(rinfo.Job).To(BeNil())
- R.mutex.Unlock()
+ Describe("Close", func() {
+ It("should fail if reqID is unknown", func() {
+ err := R.Close(noreq)
+ Expect(err).To(Equal(NotFoundError("Request")))
+ })
+ It("should fail if request is not in INPROGRESS state", func() {
+ states := []ReqState{WAIT, CANCEL, TIMEOUT, INVALID, DONE, FAILED}
+ for _, state := range states {
+ R.mutex.Lock()
+ rinfo.State = state
+ R.mutex.Unlock()
- err := R.Close(req)
- Expect(err).To(Equal(ErrInternalLogicError))
- })
- It("should fail if job's is not yet timed out", func() {
- R.mutex.Lock()
- rinfo.State = INPROGRESS
- rinfo.Job = &JobInfo{
- Timeout: time.Now().AddDate(0, 0, 1),
- }
- R.mutex.Unlock()
+ err := R.Close(req)
+ Expect(err).To(Equal(ErrModificationForbidden), "state = %s", state)
+ }
+ })
+ It("should fail if request has no job assigned", func() {
+ R.mutex.Lock()
+ rinfo.State = INPROGRESS
+ Expect(rinfo.Job).To(BeNil())
+ R.mutex.Unlock()
- err := R.Close(req)
- Expect(err).To(Equal(ErrModificationForbidden))
- })
- It("should close request and release worker", func() {
- R.mutex.Lock()
- rinfo.State = INPROGRESS
- rinfo.Job = &JobInfo{
- Timeout: time.Now().AddDate(0, 0, -1),
- }
- R.mutex.Unlock()
+ err := R.Close(req)
+ Expect(err).To(Equal(ErrInternalLogicError))
+ })
+ It("should fail if job's is not yet timed out", func() {
+ R.mutex.Lock()
+ rinfo.State = INPROGRESS
+ rinfo.Job = &JobInfo{
+ Timeout: time.Now().AddDate(0, 0, 1),
+ }
+ R.mutex.Unlock()
- err := R.Close(req)
- Expect(err).NotTo(HaveOccurred())
- Expect(rinfo.State).To(Equal(DONE))
- // TODO verify releasing worker when implemented
- })
- })
- Describe("Run", func() {
- It("should fail if reqID is unknown", func() {
- R.mutex.Lock()
- defer R.mutex.Unlock()
- Expect(R.queue.length).To(Equal(uint(1)))
- err := R.Run(noreq, WorkerUUID("TestWorker"))
- Expect(err).To(Equal(NotFoundError("Request")))
- Expect(R.queue.length).To(Equal(uint(1)))
+ err := R.Close(req)
+ Expect(err).To(Equal(ErrModificationForbidden))
+ })
+ It("should close request and release worker", func() {
+ R.mutex.Lock()
+ rinfo.State = INPROGRESS
+ rinfo.Job = &JobInfo{
+ Timeout: time.Now().AddDate(0, 0, -1),
+ }
+ R.mutex.Unlock()
+ err := R.Close(req)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(rinfo.State).To(Equal(DONE))
+ // TODO verify releasing worker when implemented
+ })
})
- It("should fail if request is not in WAIT state", func() {
- states := []ReqState{INPROGRESS, CANCEL, TIMEOUT, INVALID, DONE, FAILED}
- for _, state := range states {
+ Describe("Run", func() {
+ It("should fail if reqID is unknown", func() {
+ R.mutex.Lock()
+ defer R.mutex.Unlock()
+ Expect(R.queue.length).To(Equal(uint(1)))
+ err := R.Run(noreq, WorkerUUID("TestWorker"))
+ Expect(err).To(Equal(NotFoundError("Request")))
+ Expect(R.queue.length).To(Equal(uint(1)))
+ })
+ It("should fail if reqID is unknown during iteration", func() {
R.InitIteration()
- Expect(R.queue.length).To(Equal(uint(1)), "state = %s", state)
- rinfo.State = state
+ defer R.TerminateIteration()
+ Expect(R.iterating).To(BeTrue())
+ Expect(R.queue.length).To(Equal(uint(1)))
+ err := R.Run(noreq, WorkerUUID("TestWorker"))
+ Expect(err).To(Equal(NotFoundError("Request")))
+ Expect(R.iterating).To(BeTrue())
+ Expect(R.queue.length).To(Equal(uint(1)))
+ })
+ It("should fail if request is not in WAIT state", func() {
+ states := []ReqState{INPROGRESS, CANCEL, TIMEOUT, INVALID, DONE, FAILED}
+ for _, state := range states {
+ R.InitIteration()
+ Expect(R.queue.length).To(Equal(uint(1)), "state = %s", state)
+ rinfo.State = state
+ err := R.Run(req, WorkerUUID("TestWorker"))
+ Expect(err).To(Equal(ErrModificationForbidden), "state = %s", state)
+ Expect(R.queue.length).To(Equal(uint(1)), "state = %s", state)
+ R.TerminateIteration()
+ }
+ })
+ It("should start progress for valid reqID", func() {
+ R.InitIteration()
+ defer R.TerminateIteration()
+ Expect(R.queue.length).To(Equal(uint(1)))
err := R.Run(req, WorkerUUID("TestWorker"))
- Expect(err).To(Equal(ErrModificationForbidden), "state = %s", state)
- Expect(R.queue.length).To(Equal(uint(1)), "state = %s", state)
- R.TerminateIteration()
- }
- })
- It("should fail if reqID is unknown during iteration", func() {
- R.InitIteration()
- defer R.TerminateIteration()
- Expect(R.iterating).To(BeTrue())
- Expect(R.queue.length).To(Equal(uint(1)))
- err := R.Run(noreq, WorkerUUID("TestWorker"))
- Expect(err).To(Equal(NotFoundError("Request")))
- Expect(R.iterating).To(BeTrue())
- Expect(R.queue.length).To(Equal(uint(1)))
- })
- It("should start progress for valid reqID", func() {
- R.InitIteration()
- defer R.TerminateIteration()
- Expect(R.queue.length).To(Equal(uint(1)))
- err := R.Run(req, WorkerUUID("TestWorker"))
- Expect(err).NotTo(HaveOccurred())
- Expect(rinfo.State).To(Equal(INPROGRESS))
- Expect(R.queue.length).To(BeZero())
- })
- It("should start progress and break iterations when iterating", func() {
- R.InitIteration()
- defer R.TerminateIteration()
- Expect(R.queue.length).To(Equal(uint(1)))
- Expect(R.iterating).To(BeTrue())
- err := R.Run(req, WorkerUUID("TestWorker"))
- Expect(err).NotTo(HaveOccurred())
- Expect(rinfo.State).To(Equal(INPROGRESS))
- Expect(R.iterating).To(BeFalse())
- Expect(R.queue.length).To(BeZero())
+ Expect(err).NotTo(HaveOccurred())
+ Expect(rinfo.State).To(Equal(INPROGRESS))
+ Expect(R.queue.length).To(BeZero())
+ })
+ It("should start progress and break iterations when iterating", func() {
+ R.InitIteration()
+ defer R.TerminateIteration()
+ Expect(R.queue.length).To(Equal(uint(1)))
+ Expect(R.iterating).To(BeTrue())
+ err := R.Run(req, WorkerUUID("TestWorker"))
+ Expect(err).NotTo(HaveOccurred())
+ Expect(rinfo.State).To(Equal(INPROGRESS))
+ Expect(R.iterating).To(BeFalse())
+ Expect(R.queue.length).To(BeZero())
+ })
+ // TODO use and verify Job when Run's implementation is complete.
})
- // TODO use and verify Job when Run's implementation is complete.
})
})
})
/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package requests
import (
+ "errors"
"strconv"
"testing"
"time"
. "git.tizen.org/tools/boruta"
+ gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
},
}
-func initTest(t *testing.T) (*assert.Assertions, *ReqsCollection) {
- return assert.New(t), NewRequestQueue()
+func initTest(t *testing.T) (*assert.Assertions, *ReqsCollection, *gomock.Controller) {
+ ctrl := gomock.NewController(t)
+ wm := NewMockWorkersManager(ctrl)
+ testErr := errors.New("Test Error")
+ wm.EXPECT().TakeBestMatchingWorker(gomock.Any(), gomock.Any()).Return(WorkerUUID(""), testErr).AnyTimes()
+ return assert.New(t), NewRequestQueue(wm, nil), ctrl
+}
+
+func finiTest(rqueue *ReqsCollection, ctrl *gomock.Controller) {
+ rqueue.Finish()
+ ctrl.Finish()
}
func TestNewRequestQueue(t *testing.T) {
- assert, q := initTest(t)
+ assert, rqueue, ctrl := initTest(t)
+ defer finiTest(rqueue, ctrl)
- assert.Zero(len(q.requests))
- assert.NotNil(q.queue)
- assert.Zero(q.queue.length)
+ rqueue.mutex.RLock()
+ defer rqueue.mutex.RUnlock()
+ assert.Zero(len(rqueue.requests))
+ assert.NotNil(rqueue.queue)
+ assert.Zero(rqueue.queue.length)
}
func TestNewRequest(t *testing.T) {
- assert, rqueue := initTest(t)
+ assert, rqueue, ctrl := initTest(t)
+ defer finiTest(rqueue, ctrl)
for _, test := range requestsTests {
reqid, err := rqueue.NewRequest(test.req.Caps, test.req.Priority,
req.ValidAfter, req.Deadline)
stop := time.Now()
assert.Nil(err)
+ rqueue.mutex.RLock()
+ defer rqueue.mutex.RUnlock()
res := rqueue.requests[reqid]
assert.True(start.Before(res.ValidAfter) && stop.After(res.ValidAfter))
start = start.AddDate(0, 1, 0)
}
func TestCloseRequest(t *testing.T) {
- assert, rqueue := initTest(t)
+ assert, rqueue, ctrl := initTest(t)
+ defer finiTest(rqueue, ctrl)
req := requestsTests[0].req
// Add valid request to the queue.
assert.Nil(err)
// Cancel previously added request.
+ rqueue.mutex.RLock()
assert.EqualValues(1, rqueue.queue.length)
+ rqueue.mutex.RUnlock()
err = rqueue.CloseRequest(reqid)
assert.Nil(err)
+ rqueue.mutex.RLock()
assert.Equal(ReqState(CANCEL), rqueue.requests[reqid].State)
assert.Zero(rqueue.queue.length)
+ rqueue.mutex.RUnlock()
// Try to close non-existent request.
err = rqueue.CloseRequest(ReqID(2))
// Simulate situation where request was assigned a worker and job has begun.
reqinfo, err := rqueue.GetRequestInfo(reqid)
assert.Nil(err)
+ rqueue.mutex.Lock()
rqueue.requests[reqid].State = INPROGRESS
rqueue.queue.removeRequest(&reqinfo)
+ rqueue.mutex.Unlock()
// Close request.
err = rqueue.CloseRequest(reqid)
assert.Nil(err)
+ rqueue.mutex.RLock()
assert.EqualValues(2, len(rqueue.requests))
assert.Equal(ReqState(DONE), rqueue.requests[reqid].State)
+ rqueue.mutex.RUnlock()
// Simulation for the rest of states.
states := [...]ReqState{INVALID, CANCEL, TIMEOUT, DONE, FAILED}
assert.EqualValues(3, reqid)
reqinfo, err = rqueue.GetRequestInfo(reqid)
assert.Nil(err)
+ rqueue.mutex.Lock()
rqueue.queue.removeRequest(&reqinfo)
+ rqueue.mutex.Unlock()
for i := range states {
+ rqueue.mutex.Lock()
rqueue.requests[reqid].State = states[i]
+ rqueue.mutex.Unlock()
err = rqueue.CloseRequest(reqid)
assert.EqualValues(ErrModificationForbidden, err)
}
+ rqueue.mutex.RLock()
+ defer rqueue.mutex.RUnlock()
assert.EqualValues(3, len(rqueue.requests))
assert.EqualValues(0, rqueue.queue.length)
}
func TestUpdateRequest(t *testing.T) {
- assert, rqueue := initTest(t)
+ assert, rqueue, ctrl := initTest(t)
+ defer finiTest(rqueue, ctrl)
tmp := requestsTests[0].req
// Add valid request.
reqid, err := rqueue.NewRequest(tmp.Caps, tmp.Priority, tmp.Owner, tmp.ValidAfter, tmp.Deadline)
assert.Nil(err)
+ rqueue.mutex.RLock()
req := rqueue.requests[reqid]
+ rqueue.mutex.RUnlock()
reqBefore, err := rqueue.GetRequestInfo(reqid)
assert.Nil(err)
reqUpdate := new(ReqInfo)
+ rqueue.mutex.RLock()
*reqUpdate = *req
+ rqueue.mutex.RUnlock()
// Check noop.
err = rqueue.UpdateRequest(nil)
reqUpdate.Priority = Priority(0)
err = rqueue.UpdateRequest(reqUpdate)
assert.Nil(err)
+ rqueue.mutex.RLock()
assert.Equal(req, &reqBefore)
// Check request that doesn't exist.
*reqUpdate = *req
+ rqueue.mutex.RUnlock()
reqUpdate.ID++
err = rqueue.UpdateRequest(reqUpdate)
assert.Equal(NotFoundError("Request"), err)
+ rqueue.mutex.RLock()
reqUpdate.ID = req.ID
// Change Priority only.
reqUpdate.Priority = req.Priority - 1
+ rqueue.mutex.RUnlock()
err = rqueue.UpdateRequest(reqUpdate)
assert.Nil(err)
+ rqueue.mutex.RLock()
assert.Equal(reqUpdate.Priority, req.Priority)
+ rqueue.mutex.RUnlock()
// Change ValidAfter only.
reqUpdate.ValidAfter = yesterday
err = rqueue.UpdateRequest(reqUpdate)
assert.Nil(err)
+ rqueue.mutex.RLock()
assert.Equal(reqUpdate.ValidAfter, req.ValidAfter)
+ rqueue.mutex.RUnlock()
// Change Deadline only.
reqUpdate.Deadline = tomorrow.AddDate(0, 0, 1).UTC()
err = rqueue.UpdateRequest(reqUpdate)
assert.Nil(err)
+ rqueue.mutex.RLock()
assert.Equal(reqUpdate.Deadline, req.Deadline)
+ rqueue.mutex.RUnlock()
// Change Priority, ValidAfter and Deadline.
reqUpdate.Deadline = tomorrow
reqUpdate.ValidAfter = time.Now().Add(time.Hour)
reqUpdate.Priority = LoPrio
err = rqueue.UpdateRequest(reqUpdate)
assert.Nil(err)
+ rqueue.mutex.RLock()
assert.Equal(reqUpdate, req)
+ rqueue.mutex.RUnlock()
// Change values to the same ones that are already set.
err = rqueue.UpdateRequest(reqUpdate)
assert.Nil(err)
+ rqueue.mutex.RLock()
assert.Equal(reqUpdate, req)
+ rqueue.mutex.RUnlock()
// Change Priority to illegal value.
reqUpdate.Priority = LoPrio + 1
err = rqueue.UpdateRequest(reqUpdate)
assert.Equal(ErrPriority, err)
+ rqueue.mutex.RLock()
reqUpdate.Priority = req.Priority
+ rqueue.mutex.RUnlock()
//Change Deadline to illegal value.
reqUpdate.Deadline = yesterday
err = rqueue.UpdateRequest(reqUpdate)
err = rqueue.UpdateRequest(reqUpdate)
assert.Equal(ErrInvalidTimeRange, err)
// Change ValidAfer to illegal value.
+ rqueue.mutex.RLock()
reqUpdate.ValidAfter = req.Deadline.Add(time.Hour)
+ rqueue.mutex.RUnlock()
err = rqueue.UpdateRequest(reqUpdate)
assert.Equal(ErrInvalidTimeRange, err)
// Try to change values for other changes.
states := [...]ReqState{INVALID, CANCEL, TIMEOUT, DONE, FAILED, INPROGRESS}
for _, state := range states {
+ rqueue.mutex.Lock()
rqueue.requests[reqid].State = state
+ rqueue.mutex.Unlock()
err = rqueue.UpdateRequest(reqUpdate)
assert.Equal(ErrModificationForbidden, err)
}
}
func TestGetRequestInfo(t *testing.T) {
- assert, rqueue := initTest(t)
+ assert, rqueue, ctrl := initTest(t)
+ defer finiTest(rqueue, ctrl)
req := requestsTests[0].req
req.Job = nil
reqid, err := rqueue.NewRequest(req.Caps, req.Priority, req.Owner, req.ValidAfter, req.Deadline)
}
func TestListRequests(t *testing.T) {
- assert, rqueue := initTest(t)
+ assert, rqueue, ctrl := initTest(t)
+ defer finiTest(rqueue, ctrl)
req := requestsTests[0].req
const reqsCnt = 4
reqid, err := rqueue.NewRequest(req.Caps, req.Priority, req.Owner, req.ValidAfter, req.Deadline)
assert.Nil(err)
if i%2 == 1 {
+ rqueue.mutex.Lock()
rqueue.requests[reqid].Priority++
+ rqueue.mutex.Unlock()
}
if i > 1 {
+ rqueue.mutex.Lock()
rqueue.requests[reqid].State = DONE
+ rqueue.mutex.Unlock()
}
reqs[reqid] = true
}
}
func TestAcquireWorker(t *testing.T) {
- assert, rqueue := initTest(t)
+ assert, rqueue, ctrl := initTest(t)
+ defer finiTest(rqueue, ctrl)
req := requestsTests[0].req
empty := AccessInfo{}
states := [...]ReqState{WAIT, INVALID, CANCEL, TIMEOUT, DONE, FAILED, INPROGRESS}
for _, state := range states {
+ rqueue.mutex.Lock()
rqueue.requests[reqid].State = state
+ rqueue.mutex.Unlock()
ainfo, err := rqueue.AcquireWorker(reqid)
assert.Equal(ErrWorkerNotAssigned, err)
assert.Equal(empty, ainfo)
// AcquireWorker to succeed needs JobInfo to be set. It also needs to be
// in INPROGRESS state, which was set in the loop.
+ rqueue.mutex.Lock()
rqueue.requests[reqid].Job = new(JobInfo)
+ rqueue.mutex.Unlock()
ainfo, err = rqueue.AcquireWorker(reqid)
assert.Nil(err)
assert.Equal(empty, ainfo)
}
func TestProlongAccess(t *testing.T) {
- assert, rqueue := initTest(t)
+ assert, rqueue, ctrl := initTest(t)
+ defer finiTest(rqueue, ctrl)
req := requestsTests[0].req
// Add valid request.
states := [...]ReqState{WAIT, INVALID, CANCEL, TIMEOUT, DONE, FAILED, INPROGRESS}
for _, state := range states {
+ rqueue.mutex.Lock()
rqueue.requests[reqid].State = state
+ rqueue.mutex.Unlock()
err = rqueue.ProlongAccess(reqid)
assert.Equal(ErrWorkerNotAssigned, err)
}
// ProlongAccess to succeed needs JobInfo to be set. It also needs to be
// in INPROGRESS state, which was set in the loop.
+ rqueue.mutex.Lock()
rqueue.requests[reqid].Job = new(JobInfo)
+ rqueue.mutex.Unlock()
err = rqueue.ProlongAccess(reqid)
assert.Nil(err)
}
--- /dev/null
+// Code generated by MockGen. DO NOT EDIT.
+// Source: git.tizen.org/tools/boruta/matcher (interfaces: WorkersManager)
+
+package requests
+
+import (
+ rsa "crypto/rsa"
+ boruta "git.tizen.org/tools/boruta"
+ workers "git.tizen.org/tools/boruta/workers"
+ gomock "github.com/golang/mock/gomock"
+ net "net"
+ reflect "reflect"
+)
+
+// MockWorkersManager is a mock of WorkersManager interface
+type MockWorkersManager struct {
+ ctrl *gomock.Controller
+ recorder *MockWorkersManagerMockRecorder
+}
+
+// MockWorkersManagerMockRecorder is the mock recorder for MockWorkersManager
+type MockWorkersManagerMockRecorder struct {
+ mock *MockWorkersManager
+}
+
+// NewMockWorkersManager creates a new mock instance
+func NewMockWorkersManager(ctrl *gomock.Controller) *MockWorkersManager {
+ mock := &MockWorkersManager{ctrl: ctrl}
+ mock.recorder = &MockWorkersManagerMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockWorkersManager) EXPECT() *MockWorkersManagerMockRecorder {
+ return m.recorder
+}
+
+// GetWorkerIP mocks base method
+func (m *MockWorkersManager) GetWorkerIP(arg0 boruta.WorkerUUID) (net.IP, error) {
+ ret := m.ctrl.Call(m, "GetWorkerIP", arg0)
+ ret0, _ := ret[0].(net.IP)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetWorkerIP indicates an expected call of GetWorkerIP
+func (mr *MockWorkersManagerMockRecorder) GetWorkerIP(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkerIP", reflect.TypeOf((*MockWorkersManager)(nil).GetWorkerIP), arg0)
+}
+
+// GetWorkerKey mocks base method
+func (m *MockWorkersManager) GetWorkerKey(arg0 boruta.WorkerUUID) (rsa.PrivateKey, error) {
+ ret := m.ctrl.Call(m, "GetWorkerKey", arg0)
+ ret0, _ := ret[0].(rsa.PrivateKey)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetWorkerKey indicates an expected call of GetWorkerKey
+func (mr *MockWorkersManagerMockRecorder) GetWorkerKey(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkerKey", reflect.TypeOf((*MockWorkersManager)(nil).GetWorkerKey), arg0)
+}
+
+// PrepareWorker mocks base method
+func (m *MockWorkersManager) PrepareWorker(arg0 boruta.WorkerUUID, arg1 bool) error {
+ ret := m.ctrl.Call(m, "PrepareWorker", arg0, arg1)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// PrepareWorker indicates an expected call of PrepareWorker
+func (mr *MockWorkersManagerMockRecorder) PrepareWorker(arg0, arg1 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareWorker", reflect.TypeOf((*MockWorkersManager)(nil).PrepareWorker), arg0, arg1)
+}
+
+// SetChangeListener mocks base method
+func (m *MockWorkersManager) SetChangeListener(arg0 workers.WorkerChange) {
+ m.ctrl.Call(m, "SetChangeListener", arg0)
+}
+
+// SetChangeListener indicates an expected call of SetChangeListener
+func (mr *MockWorkersManagerMockRecorder) SetChangeListener(arg0 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetChangeListener", reflect.TypeOf((*MockWorkersManager)(nil).SetChangeListener), arg0)
+}
+
+// TakeBestMatchingWorker mocks base method
+func (m *MockWorkersManager) TakeBestMatchingWorker(arg0 boruta.Groups, arg1 boruta.Capabilities) (boruta.WorkerUUID, error) {
+ ret := m.ctrl.Call(m, "TakeBestMatchingWorker", arg0, arg1)
+ ret0, _ := ret[0].(boruta.WorkerUUID)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// TakeBestMatchingWorker indicates an expected call of TakeBestMatchingWorker
+func (mr *MockWorkersManagerMockRecorder) TakeBestMatchingWorker(arg0, arg1 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TakeBestMatchingWorker", reflect.TypeOf((*MockWorkersManager)(nil).TakeBestMatchingWorker), arg0, arg1)
+}