gitlab.com/jfprevost/gitlab-runner-notlscheck@v11.11.4+incompatible/commands/multi_test.go (about)

     1  package commands
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/mock"
    11  	"gitlab.com/gitlab-org/gitlab-runner/common"
    12  )
    13  
    14  func TestProcessRunner_BuildLimit(t *testing.T) {
    15  	cfg := common.RunnerConfig{
    16  		Limit:              2,
    17  		RequestConcurrency: 10,
    18  		RunnerSettings: common.RunnerSettings{
    19  			Executor: "multi-runner-build-limit",
    20  		},
    21  	}
    22  
    23  	jobData := common.JobResponse{
    24  		ID: 1,
    25  		Steps: []common.Step{
    26  			{
    27  				Name:         "sleep",
    28  				Script:       common.StepScript{"sleep 10"},
    29  				Timeout:      15,
    30  				When:         "",
    31  				AllowFailure: false,
    32  			},
    33  		},
    34  	}
    35  
    36  	mJobTrace := common.MockJobTrace{}
    37  	defer mJobTrace.AssertExpectations(t)
    38  	mJobTrace.On("SetFailuresCollector", mock.Anything)
    39  	mJobTrace.On("Write", mock.Anything).Return(0, nil)
    40  	mJobTrace.On("IsStdout").Return(false)
    41  	mJobTrace.On("SetCancelFunc", mock.Anything)
    42  	mJobTrace.On("SetMasked", mock.Anything)
    43  	mJobTrace.On("Success")
    44  	mJobTrace.On("Fail", mock.Anything, mock.Anything)
    45  
    46  	mNetwork := common.MockNetwork{}
    47  	defer mNetwork.AssertExpectations(t)
    48  	mNetwork.On("RequestJob", mock.Anything, mock.Anything).Return(&jobData, true)
    49  	mNetwork.On("ProcessJob", mock.Anything, mock.Anything).Return(&mJobTrace)
    50  
    51  	var runningBuilds uint32
    52  	e := common.MockExecutor{}
    53  	defer e.AssertExpectations(t)
    54  	e.On("Prepare", mock.Anything, mock.Anything, mock.Anything).Return(nil)
    55  	e.On("Cleanup").Maybe().Return()
    56  	e.On("Shell").Return(&common.ShellScriptInfo{Shell: "script-shell"})
    57  	e.On("Finish", mock.Anything).Return(nil).Maybe()
    58  	e.On("Run", mock.Anything).Run(func(args mock.Arguments) {
    59  		atomic.AddUint32(&runningBuilds, 1)
    60  
    61  		// Simulate work to fill up build queue.
    62  		time.Sleep(1 * time.Second)
    63  	}).Return(nil)
    64  
    65  	p := common.MockExecutorProvider{}
    66  	defer p.AssertExpectations(t)
    67  	p.On("Acquire", mock.Anything).Return(nil, nil)
    68  	p.On("Release", mock.Anything, mock.Anything).Return(nil).Maybe()
    69  	p.On("CanCreate").Return(true).Once()
    70  	p.On("GetDefaultShell").Return("bash").Once()
    71  	p.On("GetFeatures", mock.Anything).Return(nil)
    72  	p.On("Create").Return(&e)
    73  
    74  	common.RegisterExecutor("multi-runner-build-limit", &p)
    75  
    76  	cmd := RunCommand{
    77  		network:      &mNetwork,
    78  		buildsHelper: newBuildsHelper(),
    79  		configOptionsWithListenAddress: configOptionsWithListenAddress{
    80  			configOptions: configOptions{
    81  				config: &common.Config{
    82  					User: "git",
    83  				},
    84  			},
    85  		},
    86  	}
    87  
    88  	runners := make(chan *common.RunnerConfig)
    89  
    90  	// Start 5 builds.
    91  	wg := sync.WaitGroup{}
    92  	wg.Add(5)
    93  	for i := 0; i < 5; i++ {
    94  		go func(i int) {
    95  			defer wg.Done()
    96  
    97  			cmd.processRunner(i, &cfg, runners)
    98  		}(i)
    99  	}
   100  
   101  	// Wait until at least two builds have started.
   102  	for atomic.LoadUint32(&runningBuilds) < 2 {
   103  		time.Sleep(10 * time.Millisecond)
   104  	}
   105  
   106  	err := cmd.processRunner(6, &cfg, runners)
   107  	assert.EqualError(t, err, "failed to request job, runner limit met")
   108  
   109  	// Wait for all builds to finish.
   110  	wg.Wait()
   111  }