// Copyright 2020 Amazon.com, Inc. or its affiliates. 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. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing // permissions and limitations under the License // // Package tester is responsible for initiating testing based on the test stage value passed package tester import ( "fmt" "testing" "time" "github.com/aws/amazon-ssm-agent/agent/context" "github.com/aws/amazon-ssm-agent/agent/contracts" contextmocks "github.com/aws/amazon-ssm-agent/agent/mocks/context" testCommon "github.com/aws/amazon-ssm-agent/agent/update/tester/common" "github.com/aws/amazon-ssm-agent/agent/update/tester/common/mocks" "github.com/stretchr/testify/assert" ) func TestStartTests_Timeout(t *testing.T) { oldRunTests := runTests oldDefaultTimeout := defaultTestTimeOut defer func() { runTests = oldRunTests defaultTestTimeOut = oldDefaultTimeout }() runTestDoneChan := make(chan bool, 1) var doneChanHook, stopChanHook chan bool runTests = func(_ context.T, _ []testCommon.ITestCase, _ func(contracts.ResultStatus, string), doneChan chan bool, stopChan chan bool) { doneChanHook = doneChan stopChanHook = stopChan time.Sleep(time.Millisecond * 500) select { case <-stopChan: default: assert.Fail(t, "Expected stopChan to be closed") } close(doneChan) close(runTestDoneChan) } defaultTestTimeOut = time.Millisecond StartTests(contextmocks.NewMockDefault(), func(contracts.ResultStatus, string) {}) select { case <-runTestDoneChan: case <-time.After(time.Second): assert.Fail(t, "Expected runTestDoneChan to be closed sooner") } select { case <-doneChanHook: default: assert.Fail(t, "Expected doneChan to be closed") } select { case <-stopChanHook: default: assert.Fail(t, "Expected stopChan to be closed") } } func TestStartTests_PanicRecovered(t *testing.T) { oldGetUpdateTests := getUpdateTests defer func() { getUpdateTests = oldGetUpdateTests }() getUpdateTests = func(context context.T) []testCommon.ITestCase { panic("Something Happened") } StartTests(contextmocks.NewMockDefault(), func(contracts.ResultStatus, string) {}) } func TestStartTests_Success(t *testing.T) { oldRunTests := runTests oldDefaultTimeout := defaultTestTimeOut defer func() { runTests = oldRunTests defaultTestTimeOut = oldDefaultTimeout }() var doneChanHook, stopChanHook chan bool runTests = func(_ context.T, _ []testCommon.ITestCase, _ func(contracts.ResultStatus, string), doneChan chan bool, stopChan chan bool) { doneChanHook = doneChan stopChanHook = stopChan close(doneChan) } start := time.Now() defaultTestTimeOut = time.Second StartTests(contextmocks.NewMockDefault(), func(contracts.ResultStatus, string) {}) assert.Less(t, time.Since(start), time.Millisecond*500) select { case <-doneChanHook: default: assert.Fail(t, "Expected doneChan to be closed") } select { case <-stopChanHook: default: assert.Fail(t, "Expected stopChan to be closed") } } func TestRunTests_NoTestToRun(t *testing.T) { stopChan := make(chan bool, 1) doneChan := make(chan bool, 1) runTests(contextmocks.NewMockDefault(), []testCommon.ITestCase{}, func(contracts.ResultStatus, string) {}, doneChan, stopChan) select { case <-stopChan: assert.Fail(t, "expected stopChan to still be open") default: } select { case <-doneChan: default: assert.Fail(t, "expected doneChan to be closed") } } func TestRunTests_StopBeforeAnyTestRuns(t *testing.T) { testMock := &mocks.ITestCase{} testCases := []testCommon.ITestCase{ testMock, } stopChan := make(chan bool, 1) close(stopChan) doneChan := make(chan bool, 1) runTests(contextmocks.NewMockDefault(), testCases, func(contracts.ResultStatus, string) {}, doneChan, stopChan) select { case <-stopChan: default: assert.Fail(t, "expected stopChan to be closed") } select { case <-doneChan: default: assert.Fail(t, "expected doneChan to be closed") } testMock.AssertExpectations(t) } func TestRunTests_StopAfterFirstTest(t *testing.T) { testMock := &mocks.ITestCase{} testCases := []testCommon.ITestCase{ testMock, testMock, } stopChan := make(chan bool, 1) doneChan := make(chan bool, 1) testMock.On("ShouldRunTest").Return(func() bool { close(stopChan) return false }).Once() runTests(contextmocks.NewMockDefault(), testCases, func(contracts.ResultStatus, string) {}, doneChan, stopChan) select { case <-stopChan: default: assert.Fail(t, "expected stopChan to be closed") } select { case <-doneChan: default: assert.Fail(t, "expected doneChan to be closed") } testMock.AssertExpectations(t) } func TestRunTests_RunAllTests(t *testing.T) { testMock := &mocks.ITestCase{} testCases := []testCommon.ITestCase{ testMock, testMock, testMock, testMock, } reportResults := func(testResult contracts.ResultStatus, testName string) { switch testName { case "TestName1": assert.Equal(t, contracts.ResultStatusTestFailure, testResult) case "TestName2": assert.Equal(t, contracts.ResultStatusTestPass, testResult) case "TestName3": assert.Fail(t, "TestName3 should not report results because of unsupported result") default: assert.Fail(t, "Unexpected test name: "+testName) } } stopChan := make(chan bool, 1) doneChan := make(chan bool, 1) // First test skip testMock.On("ShouldRunTest").Return(false).Once() // Second test pass testMock.On("ShouldRunTest").Return(true).Once() testMock.On("GetTestCaseName").Return("TestName1").Once() testMock.On("Initialize").Return().Once() testMock.On("ExecuteTestCase").Return(testCommon.TestOutput{Err: nil, Result: testCommon.TestCaseFail}).Once() testMock.On("CleanupTestCase").Return().Once() // Third test fail testMock.On("ShouldRunTest").Return(true).Once() testMock.On("GetTestCaseName").Return("TestName2").Once() testMock.On("Initialize").Return().Once() testMock.On("ExecuteTestCase").Return(testCommon.TestOutput{Err: nil, Result: testCommon.TestCasePass}).Once() testMock.On("CleanupTestCase").Return().Once() // Fourth pass unexpected result testMock.On("ShouldRunTest").Return(true).Once() testMock.On("GetTestCaseName").Return("TestName3").Once() testMock.On("Initialize").Return().Once() testMock.On("ExecuteTestCase").Return(testCommon.TestOutput{Err: fmt.Errorf("SomeError"), Result: "SomeRandomResult"}).Once() testMock.On("CleanupTestCase").Return().Once() runTests(contextmocks.NewMockDefault(), testCases, reportResults, doneChan, stopChan) select { case <-stopChan: assert.Fail(t, "expected stopChan to still be open") default: } select { case <-doneChan: default: assert.Fail(t, "expected doneChan to be closed") } testMock.AssertExpectations(t) } func TestRunTests_PanicRecovery(t *testing.T) { testMock := &mocks.ITestCase{} testCases := []testCommon.ITestCase{ testMock, testMock, } reportResults := func(testResult contracts.ResultStatus, testName string) { assert.Fail(t, "No test should be reported when first one fails") } stopChan := make(chan bool, 1) doneChan := make(chan bool, 1) // Panic recove testMock.On("ShouldRunTest").Return(func() bool { panic("SomeRandomPanicInTest") }).Once() runTests(contextmocks.NewMockDefault(), testCases, reportResults, doneChan, stopChan) select { case <-stopChan: assert.Fail(t, "expected stopChan to still be open") default: } select { case <-doneChan: default: assert.Fail(t, "expected doneChan to be closed") } testMock.AssertExpectations(t) }