func (s *repositorySuite) TestAddHandlerGenerator(c *C) { repository := newRepository() var calledContext *Context mockHandlerGenerator := func(context *Context) Handler { calledContext = context return hooktest.NewMockHandler() } // Verify that a handler generator can be added to the repository repository.addHandlerGenerator(regexp.MustCompile("test-hook"), mockHandlerGenerator) state := state.New(nil) state.Lock() task := state.NewTask("test-task", "my test task") setup := &HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} context := &Context{task: task, setup: setup} state.Unlock() c.Assert(context, NotNil) // Verify that the handler can be generated handlers := repository.generateHandlers(context) c.Check(handlers, HasLen, 1) c.Check(calledContext, DeepEquals, context) // Add another handler repository.addHandlerGenerator(regexp.MustCompile(".*-hook"), mockHandlerGenerator) // Verify that two handlers are generated for the test-hook, now handlers = repository.generateHandlers(context) c.Check(handlers, HasLen, 2) c.Check(calledContext, DeepEquals, context) }
func (s *hookManagerSuite) TestHookTaskHandlerErrorError(c *C) { // Register a handler generator for the "test-hook" hook mockHandler := hooktest.NewMockHandler() mockHandler.ErrorError = true mockHandlerGenerator := func(context *hookstate.Context) hookstate.Handler { return mockHandler } // Force the snap command to simply exit 1, so the handler Error() runs s.command = testutil.MockCommand(c, "snap", "exit 1") s.manager.Register(regexp.MustCompile("test-hook"), mockHandlerGenerator) s.manager.Ensure() s.manager.Wait() s.state.Lock() defer s.state.Unlock() c.Check(mockHandler.BeforeCalled, Equals, true) c.Check(mockHandler.DoneCalled, Equals, false) c.Check(mockHandler.ErrorCalled, Equals, true) c.Check(s.task.Kind(), Equals, "run-hook") c.Check(s.task.Status(), Equals, state.ErrorStatus) c.Check(s.change.Status(), Equals, state.ErrorStatus) checkTaskLogContains(c, s.task, regexp.MustCompile(".*Error failed at user request.*")) }
func (s *hookManagerSuite) TestHookTask(c *C) { // Register a handler generator for the "test-hook" hook var calledContext *hookstate.Context mockHandler := hooktest.NewMockHandler() mockHandlerGenerator := func(context *hookstate.Context) hookstate.Handler { calledContext = context return mockHandler } s.manager.Register(regexp.MustCompile("test-hook"), mockHandlerGenerator) s.manager.Ensure() s.manager.Wait() s.state.Lock() defer s.state.Unlock() c.Assert(calledContext, NotNil, Commentf("Expected handler generator to be called with a valid context")) c.Check(calledContext.SnapName(), Equals, "test-snap") c.Check(calledContext.SnapRevision(), Equals, snap.R(1)) c.Check(calledContext.HookName(), Equals, "test-hook") c.Check(s.command.Calls(), DeepEquals, [][]string{{ "snap", "run", "--hook", "test-hook", "-r", "1", "test-snap", }}) c.Check(mockHandler.BeforeCalled, Equals, true) c.Check(mockHandler.DoneCalled, Equals, true) c.Check(mockHandler.ErrorCalled, Equals, false) c.Check(s.task.Kind(), Equals, "run-hook") c.Check(s.task.Status(), Equals, state.DoneStatus) c.Check(s.change.Status(), Equals, state.DoneStatus) }
func (s *getSuite) TestGetTests(c *C) { for _, test := range getTests { c.Logf("Test: %s", test.args) mockHandler := hooktest.NewMockHandler() state := state.New(nil) state.Lock() task := state.NewTask("test-task", "my test task") setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} var err error mockContext, err := hookstate.NewContext(task, setup, mockHandler) c.Check(err, IsNil) // Initialize configuration t := configstate.NewTransaction(state) t.Set("test-snap", "test-key1", "test-value1") t.Set("test-snap", "test-key2", 2) t.Commit() state.Unlock() stdout, stderr, err := ctlcmd.Run(mockContext, strings.Fields(test.args)) if test.error != "" { c.Check(err, ErrorMatches, test.error) } else { c.Check(err, IsNil) c.Check(string(stderr), Equals, "") c.Check(string(stdout), Equals, test.stdout) } } }
func (s *hookManagerSuite) TestHookTaskCorrectlyIncludesContext(c *C) { // Register a handler generator for the "test-hook" hook mockHandler := hooktest.NewMockHandler() mockHandlerGenerator := func(context *hookstate.Context) hookstate.Handler { return mockHandler } // Force the snap command to exit with a failure and print to stderr so we // can catch and verify it. s.command = testutil.MockCommand( c, "snap", ">&2 echo \"SNAP_CONTEXT=$SNAP_CONTEXT\"; exit 1") s.manager.Register(regexp.MustCompile("test-hook"), mockHandlerGenerator) s.manager.Ensure() s.manager.Wait() s.state.Lock() defer s.state.Unlock() c.Check(mockHandler.BeforeCalled, Equals, true) c.Check(mockHandler.DoneCalled, Equals, false) c.Check(mockHandler.ErrorCalled, Equals, true) c.Check(s.task.Kind(), Equals, "run-hook") c.Check(s.task.Status(), Equals, state.ErrorStatus) c.Check(s.change.Status(), Equals, state.ErrorStatus) checkTaskLogContains(c, s.task, regexp.MustCompile(".*SNAP_CONTEXT=\\S+")) }
func (s *setSuite) SetUpTest(c *C) { s.mockHandler = hooktest.NewMockHandler() state := state.New(nil) state.Lock() defer state.Unlock() task := state.NewTask("test-task", "my test task") setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} var err error s.mockContext, err = hookstate.NewContext(task, setup, s.mockHandler) c.Assert(err, IsNil) }
func (s *hookManagerSuite) TestHookWithMultipleHandlersIsError(c *C) { // Register multiple times for this hook s.manager.Register(regexp.MustCompile("configure"), func(context *hookstate.Context) hookstate.Handler { return hooktest.NewMockHandler() }) s.manager.Ensure() s.manager.Wait() s.state.Lock() defer s.state.Unlock() c.Check(s.task.Kind(), Equals, "run-hook") c.Check(s.task.Status(), Equals, state.ErrorStatus) c.Check(s.change.Status(), Equals, state.ErrorStatus) checkTaskLogContains(c, s.task, `.*2 handlers registered for hook "configure".*`) }
func (s *getSuite) SetUpTest(c *C) { s.mockHandler = hooktest.NewMockHandler() state := state.New(nil) state.Lock() defer state.Unlock() task := state.NewTask("test-task", "my test task") setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} var err error s.mockContext, err = hookstate.NewContext(task, setup, s.mockHandler) c.Assert(err, IsNil) // Initialize configuration transaction := configstate.NewTransaction(state) transaction.Set("test-snap", "initial-key", "initial-value") transaction.Commit() }
func (s *hookManagerSuite) SetUpTest(c *C) { dirs.SetRootDir(c.MkDir()) s.state = state.New(nil) manager, err := hookstate.Manager(s.state) c.Assert(err, IsNil) s.manager = manager hooksup := &hookstate.HookSetup{ Snap: "test-snap", Hook: "configure", Revision: snap.R(1), } initialContext := map[string]interface{}{ "test-key": "test-value", } s.state.Lock() s.task = hookstate.HookTask(s.state, "test summary", hooksup, initialContext) c.Assert(s.task, NotNil, Commentf("Expected HookTask to return a task")) s.change = s.state.NewChange("kind", "summary") s.change.AddTask(s.task) sideInfo := &snap.SideInfo{RealName: "test-snap", SnapID: "some-snap-id", Revision: snap.R(1)} snaptest.MockSnap(c, snapYaml, snapContents, sideInfo) snapstate.Set(s.state, "test-snap", &snapstate.SnapState{ Active: true, Sequence: []*snap.SideInfo{sideInfo}, Current: snap.R(1), }) s.state.Unlock() s.command = testutil.MockCommand(c, "snap", "") s.context = nil s.mockHandler = hooktest.NewMockHandler() s.manager.Register(regexp.MustCompile("configure"), func(context *hookstate.Context) hookstate.Handler { s.context = context return s.mockHandler }) }
func (s *hookManagerSuite) TestHookTaskCanKillHook(c *C) { // Register a handler generator for the "test-hook" hook mockHandler := hooktest.NewMockHandler() mockHandlerGenerator := func(context *hookstate.Context) hookstate.Handler { return mockHandler } // Force the snap command to hang s.command = testutil.MockCommand(c, "snap", "while true; do sleep 1; done") s.manager.Register(regexp.MustCompile("test-hook"), mockHandlerGenerator) s.manager.Ensure() completed := make(chan struct{}) go func() { s.manager.Wait() close(completed) }() // Abort the change, which should kill the hanging hook, and wait for the // task to complete. s.state.Lock() s.change.Abort() s.state.Unlock() s.manager.Ensure() <-completed s.state.Lock() defer s.state.Unlock() c.Check(mockHandler.BeforeCalled, Equals, true) c.Check(mockHandler.DoneCalled, Equals, false) c.Check(mockHandler.ErrorCalled, Equals, true) c.Check(mockHandler.Err, ErrorMatches, ".*hook \"test-hook\" aborted.*") c.Check(s.task.Kind(), Equals, "run-hook") c.Check(s.task.Status(), Equals, state.ErrorStatus) c.Check(s.change.Status(), Equals, state.ErrorStatus) checkTaskLogContains(c, s.task, regexp.MustCompile(".*hook \"test-hook\" aborted.*")) }