// Copyright 2015 Google Inc. 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.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License 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.

// +build !windows

package limits

import (
	"errors"
	"math"
	"syscall"
	"testing"
)

type rlimitFunc func(int, *syscall.Rlimit) error

func TestSetupFDLimits(t *testing.T) {
	tests := []struct {
		desc    string
		getFunc rlimitFunc
		setFunc rlimitFunc
		wantFDs uint64
		wantErr bool
	}{
		{
			desc: "Getrlimit fails",
			getFunc: func(_ int, _ *syscall.Rlimit) error {
				return errors.New("failed to read rlimit for max file descriptors")
			},
			setFunc: func(_ int, _ *syscall.Rlimit) error {
				panic("shouldn't be called")
			},
			wantFDs: 0,
			wantErr: true,
		},
		{
			desc: "Getrlimit max is less than wantFDs",
			getFunc: func(_ int, rlim *syscall.Rlimit) error {
				rlim.Cur = 512
				rlim.Max = 512
				return nil
			},
			setFunc: func(_ int, rlim *syscall.Rlimit) error {
				if rlim.Cur != 1024 || rlim.Max != 1024 {
					return errors.New("setrlimit called with unexpected value")
				}
				return nil
			},
			wantFDs: 1024,
			wantErr: false,
		},
		{
			desc: "Getrlimit returns rlim_infinity",
			getFunc: func(_ int, rlim *syscall.Rlimit) error {
				rlim.Cur = math.MaxUint64
				rlim.Max = math.MaxUint64
				return nil
			},
			setFunc: func(_ int, _ *syscall.Rlimit) error {
				panic("shouldn't be called")
			},
			wantFDs: 1024,
			wantErr: false,
		},
		{
			desc: "Getrlimit cur is greater than wantFDs",
			getFunc: func(_ int, rlim *syscall.Rlimit) error {
				rlim.Cur = 512
				rlim.Max = 512
				return nil
			},
			setFunc: func(_ int, _ *syscall.Rlimit) error {
				panic("shouldn't be called")
			},
			wantFDs: 256,
			wantErr: false,
		},
		{
			desc: "Setrlimit fails",
			getFunc: func(_ int, rlim *syscall.Rlimit) error {
				rlim.Cur = 128
				rlim.Max = 512
				return nil
			},
			setFunc: func(_ int, _ *syscall.Rlimit) error {
				return errors.New("failed to set rlimit for max file descriptors")
			},
			wantFDs: 256,
			wantErr: true,
		},
		{
			desc: "Success",
			getFunc: func(_ int, rlim *syscall.Rlimit) error {
				rlim.Cur = 128
				rlim.Max = 512
				return nil
			},
			setFunc: func(_ int, _ *syscall.Rlimit) error {
				return nil
			},
			wantFDs: 256,
			wantErr: false,
		},
	}

	for _, test := range tests {
		oldGetFunc := syscallGetrlimit
		syscallGetrlimit = test.getFunc
		defer func() {
			syscallGetrlimit = oldGetFunc
		}()

		oldSetFunc := syscallSetrlimit
		syscallSetrlimit = test.setFunc
		defer func() {
			syscallSetrlimit = oldSetFunc
		}()

		gotErr := SetupFDLimits(test.wantFDs)
		if (gotErr != nil) != test.wantErr {
			t.Errorf("%s: limits.SetupFDLimits(%d) returned error %v, wantErr %v", test.desc, test.wantFDs, gotErr, test.wantErr)
		}
	}
}
