katatestutils: Fix version check bug

Fix version comparison bug whereby the specified operator was being
applied to the wrong version number.

The version handling for distro and kernel versions was incorrect. This
was not clear as the internal logic was rather difficult to understand.
Renaming `constraintInvalid()` to `constraintValid()` and updating
`NotValid()` correspondingly makes the logic clearer and exposed the
bug, allowing it to be fixed.

Added two new tests to ensure correct behaviour:

- `TestConstraintNotValidKernelVersion()`
- `TestConstraintNotValidDistroVersion()`

Fixes #1653.

Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
This commit is contained in:
James O. D. Hunt 2019-05-09 15:31:49 +01:00
parent 1bec735cb6
commit 2bc03f23e0
3 changed files with 205 additions and 41 deletions

View File

@ -184,13 +184,13 @@ func (tc *TestConstraint) handleDistroName(name string, op Operator) (result Res
// handleDistroVersion checks that the current distro version is compatible with // handleDistroVersion checks that the current distro version is compatible with
// the constraint specified by the arguments. // the constraint specified by the arguments.
func (tc *TestConstraint) handleDistroVersion(version string, op Operator) (result Result, err error) { func (tc *TestConstraint) handleDistroVersion(version string, op Operator) (result Result, err error) {
return handleVersionType("distro", version, op, tc.DistroVersion) return handleVersionType("distro", tc.DistroVersion, op, version)
} }
// handleKernelVersion checks that the current kernel version is compatible with // handleKernelVersion checks that the current kernel version is compatible with
// the constraint specified by the arguments. // the constraint specified by the arguments.
func (tc *TestConstraint) handleKernelVersion(version string, op Operator) (result Result, err error) { func (tc *TestConstraint) handleKernelVersion(version string, op Operator) (result Result, err error) {
return handleVersionType("kernel", version, op, tc.KernelVersion) return handleVersionType("kernel", tc.KernelVersion, op, version)
} }
// handleVersionType checks that the current and new versions are compatible with // handleVersionType checks that the current and new versions are compatible with
@ -250,7 +250,7 @@ func handleVersionType(versionName, newVersion string, op Operator, currentVersi
} }
descr := fmt.Sprintf("need %s version %s %q, got version %q", descr := fmt.Sprintf("need %s version %s %q, got version %q",
versionName, op, newVersion, currentVersion) versionName, op, currentVersion, newVersion)
result = Result{ result = Result{
Description: descr, Description: descr,
@ -432,9 +432,9 @@ func (tc *TestConstraint) handleResults(result Result, err error) {
} }
} }
// constraintInvalid handles the specified constraint, returning true if the // constraintValid handles the specified constraint, returning true if the
// constraint fails, else false. // constraint is valid, else false.
func (tc *TestConstraint) constraintInvalid(fn Constraint) bool { func (tc *TestConstraint) constraintValid(fn Constraint) bool {
c := Constraints{} c := Constraints{}
// Call the constraint function that sets the Constraints values // Call the constraint function that sets the Constraints values
@ -449,7 +449,7 @@ func (tc *TestConstraint) constraintInvalid(fn Constraint) bool {
result, err := tc.handleUID(c.UID, c.Operator) result, err := tc.handleUID(c.UID, c.Operator)
tc.handleResults(result, err) tc.handleResults(result, err)
if !result.Success { if !result.Success {
return true return false
} }
} }
@ -457,7 +457,7 @@ func (tc *TestConstraint) constraintInvalid(fn Constraint) bool {
result, err := tc.handleDistroName(c.DistroName, c.Operator) result, err := tc.handleDistroName(c.DistroName, c.Operator)
tc.handleResults(result, err) tc.handleResults(result, err)
if !result.Success { if !result.Success {
return true return false
} }
} }
@ -465,7 +465,7 @@ func (tc *TestConstraint) constraintInvalid(fn Constraint) bool {
result, err := tc.handleDistroVersion(c.DistroVersion, c.Operator) result, err := tc.handleDistroVersion(c.DistroVersion, c.Operator)
tc.handleResults(result, err) tc.handleResults(result, err)
if !result.Success { if !result.Success {
return true return false
} }
} }
@ -474,11 +474,11 @@ func (tc *TestConstraint) constraintInvalid(fn Constraint) bool {
result, err := tc.handleKernelVersion(c.KernelVersion, c.Operator) result, err := tc.handleKernelVersion(c.KernelVersion, c.Operator)
tc.handleResults(result, err) tc.handleResults(result, err)
if !result.Success { if !result.Success {
return true return false
} }
} }
// Constraint is valid // Constraint is valid
return false return true
} }

View File

@ -118,8 +118,8 @@ func (tc *TestConstraint) NotValid(constraints ...Constraint) bool {
tc.Issue = "" tc.Issue = ""
for _, c := range constraints { for _, c := range constraints {
invalid := tc.constraintInvalid(c) valid := tc.constraintValid(c)
if invalid { if !valid {
return true return true
} }
} }

View File

@ -196,19 +196,34 @@ func semverBumpVersion(ver semver.Version, bumpMajor, bumpMinor, bumpPatch bool)
return ver.String(), nil return ver.String(), nil
} }
// incrementVersion increments the specified version and returns the // changeVersion modifies the specified version and returns the
// string representation. // string representation. If decrement is true the returned version is smaller
func incrementVersion(version string) (string, error) { // than the specified version, else it is larger.
func changeVersion(version string, decrement bool) (string, error) {
operand := int64(1)
if decrement {
operand = -1
}
// Is it an integer? // Is it an integer?
intResult, err := strconv.ParseUint(version, 10, 0) intResult, err := strconv.ParseUint(version, 10, 0)
if err == nil { if err == nil {
return fmt.Sprintf("%d", intResult+1), nil if intResult == 0 && decrement {
return "", fmt.Errorf("cannot decrement integer version with value zero")
}
return fmt.Sprintf("%d", uint64(int64(intResult)+operand)), nil
} }
// Is it a float? // Is it a float?
floatResult, err := strconv.ParseFloat(version, 32) floatResult, err := strconv.ParseFloat(version, 32)
if err == nil { if err == nil {
return fmt.Sprintf("%f", floatResult+1), nil if int(floatResult) == 0 && decrement {
return "", fmt.Errorf("cannot decrement integer part of floating point version with value zero: %v", version)
}
return fmt.Sprintf("%f", floatResult+float64(operand)), nil
} }
// Not an int nor a float, so it must be a semantic version // Not an int nor a float, so it must be a semantic version
@ -218,14 +233,36 @@ func incrementVersion(version string) (string, error) {
return "", err return "", err
} }
err = ver.IncrementMajor() if decrement {
if err != nil { // the semver package only provides increment operations, so
return "", err // handle decrement ourselves.
major := ver.Major
if major == 0 {
return "", fmt.Errorf("cannot decrement semver with zero major version: %+v", version)
}
major--
ver.Major = major
} else {
err = ver.IncrementMajor()
if err != nil {
return "", err
}
} }
return ver.String(), nil return ver.String(), nil
} }
func incrementVersion(version string) (string, error) {
return changeVersion(version, false)
}
func decrementVersion(version string) (string, error) {
return changeVersion(version, true)
}
// testGetDistro is an alternative implementation of getDistroDetails() used // testGetDistro is an alternative implementation of getDistroDetails() used
// for testing. // for testing.
func testGetDistro() (name, version string, err error) { func testGetDistro() (name, version string, err error) {
@ -550,16 +587,16 @@ func TestConstraintHandleDistroVersion(t *testing.T) {
{higherVersion, eqOperator, Result{Success: false}, false}, {higherVersion, eqOperator, Result{Success: false}, false},
{distroVersion, gtOperator, Result{Success: false}, false}, {distroVersion, gtOperator, Result{Success: false}, false},
{higherVersion, gtOperator, Result{Success: true}, false}, {higherVersion, gtOperator, Result{Success: false}, false},
{distroVersion, geOperator, Result{Success: true}, false}, {distroVersion, geOperator, Result{Success: true}, false},
{higherVersion, geOperator, Result{Success: true}, false}, {higherVersion, geOperator, Result{Success: false}, false},
{distroVersion, ltOperator, Result{Success: false}, false}, {distroVersion, ltOperator, Result{Success: false}, false},
{higherVersion, ltOperator, Result{Success: false}, false}, {higherVersion, ltOperator, Result{Success: true}, false},
{distroVersion, leOperator, Result{Success: true}, false}, {distroVersion, leOperator, Result{Success: true}, false},
{higherVersion, leOperator, Result{Success: false}, false}, {higherVersion, leOperator, Result{Success: true}, false},
{distroVersion, neOperator, Result{Success: false}, false}, {distroVersion, neOperator, Result{Success: false}, false},
{higherVersion, neOperator, Result{Success: true}, false}, {higherVersion, neOperator, Result{Success: true}, false},
@ -773,24 +810,24 @@ func TestConstraintHandleKernelVersion(t *testing.T) {
{kernelVersion, neOperator, Result{Success: false}, false}, {kernelVersion, neOperator, Result{Success: false}, false},
{newerMajor, eqOperator, Result{Success: false}, false}, {newerMajor, eqOperator, Result{Success: false}, false},
{newerMajor, geOperator, Result{Success: true}, false}, {newerMajor, geOperator, Result{Success: false}, false},
{newerMajor, gtOperator, Result{Success: true}, false}, {newerMajor, gtOperator, Result{Success: false}, false},
{newerMajor, ltOperator, Result{Success: false}, false}, {newerMajor, ltOperator, Result{Success: true}, false},
{newerMajor, leOperator, Result{Success: false}, false}, {newerMajor, leOperator, Result{Success: true}, false},
{newerMajor, neOperator, Result{Success: true}, false}, {newerMajor, neOperator, Result{Success: true}, false},
{newerMinor, eqOperator, Result{Success: false}, false}, {newerMinor, eqOperator, Result{Success: false}, false},
{newerMinor, geOperator, Result{Success: true}, false}, {newerMinor, geOperator, Result{Success: false}, false},
{newerMinor, gtOperator, Result{Success: true}, false}, {newerMinor, gtOperator, Result{Success: false}, false},
{newerMinor, ltOperator, Result{Success: false}, false}, {newerMinor, ltOperator, Result{Success: true}, false},
{newerMinor, leOperator, Result{Success: false}, false}, {newerMinor, leOperator, Result{Success: true}, false},
{newerMinor, neOperator, Result{Success: true}, false}, {newerMinor, neOperator, Result{Success: true}, false},
{newerPatch, eqOperator, Result{Success: false}, false}, {newerPatch, eqOperator, Result{Success: false}, false},
{newerPatch, geOperator, Result{Success: true}, false}, {newerPatch, geOperator, Result{Success: false}, false},
{newerPatch, gtOperator, Result{Success: true}, false}, {newerPatch, gtOperator, Result{Success: false}, false},
{newerPatch, ltOperator, Result{Success: false}, false}, {newerPatch, ltOperator, Result{Success: true}, false},
{newerPatch, leOperator, Result{Success: false}, false}, {newerPatch, leOperator, Result{Success: true}, false},
{newerPatch, neOperator, Result{Success: true}, false}, {newerPatch, neOperator, Result{Success: true}, false},
} }
@ -1145,7 +1182,134 @@ func TestConstraintNotValid(t *testing.T) {
} }
func TestConstraintConstraintInvalid(t *testing.T) { func TestConstraintNotValidKernelVersion(t *testing.T) {
assert := assert.New(t)
assert.NotNil(kernelVersion)
// Generate new kernel versions for testing purposes based on the
// current kernel version.
higherVersion, err := incrementVersion(kernelVersion)
assert.NoError(err)
assert.NotEqual(kernelVersion, higherVersion)
lowerVersion, err := decrementVersion(kernelVersion)
assert.NoError(err)
assert.NotEqual(kernelVersion, lowerVersion)
// Antique kernel version numbers.
//
// Note: Not all are actually real kernel releases - we're just trying
// to do a thorough test.
lowKernelVersions := []string{
"0.0.0",
"0.0.1",
"1.0.0",
"1.0.6-1.1.0",
"2.0.0",
"2.6.0",
lowerVersion,
}
// Host kernel is expected to be newer than all the low kernel versions
for _, debug := range []bool{true, false} {
tc := NewTestConstraint(debug)
for _, ver := range lowKernelVersions {
result := tc.NotValid(NeedKernelVersionEquals(ver))
assert.True(result)
result = tc.NotValid(NeedKernelVersionLE(ver))
assert.True(result)
result = tc.NotValid(NeedKernelVersionLT(ver))
assert.True(result)
result = tc.NotValid(NeedKernelVersionGT(ver))
assert.False(result)
result = tc.NotValid(NeedKernelVersionGE(ver))
assert.False(result)
result = tc.NotValid(NeedKernelVersionNotEquals(ver))
assert.False(result)
}
}
// Ridiculously high kernel version numbers. The host kernel is
// expected to never reach these values.
highKernelVersions := []string{
higherVersion,
"999.0.0",
"999.0.999",
"999.999.999",
"1024.0.0",
}
for _, debug := range []bool{true, false} {
tc := NewTestConstraint(debug)
for _, ver := range highKernelVersions {
result := tc.NotValid(NeedKernelVersionEquals(ver))
assert.True(result)
result = tc.NotValid(NeedKernelVersionGE(ver))
assert.True(result)
result = tc.NotValid(NeedKernelVersionGT(ver))
assert.True(result)
result = tc.NotValid(NeedKernelVersionLE(ver))
assert.False(result)
result = tc.NotValid(NeedKernelVersionLT(ver))
assert.False(result)
result = tc.NotValid(NeedKernelVersionNotEquals(ver))
assert.False(result)
}
}
}
func TestConstraintNotValidDistroVersion(t *testing.T) {
assert := assert.New(t)
assert.NotNil(distroVersion)
// Generate new distro versions for testing purposes based on the
// current kernel version.
higherVersion, err := incrementVersion(distroVersion)
assert.NoError(err)
assert.NotEqual(distroVersion, higherVersion)
lowerVersion, err := decrementVersion(distroVersion)
assert.NoError(err)
assert.NotEqual(distroVersion, lowerVersion)
for _, debug := range []bool{true, false} {
tc := NewTestConstraint(debug)
result := tc.NotValid(NeedDistroVersionEquals(higherVersion))
assert.True(result)
result = tc.NotValid(NeedDistroVersionEquals(distroVersion))
assert.False(result)
result = tc.NotValid(NeedDistroVersionLT(higherVersion))
assert.False(result)
result = tc.NotValid(NeedDistroVersionLT(distroVersion))
assert.True(result)
result = tc.NotValid(NeedDistroVersionGT(higherVersion))
assert.True(result)
result = tc.NotValid(NeedDistroVersionGT(distroVersion))
assert.True(result)
}
}
func TestConstraintConstraintValid(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
type testData struct { type testData struct {
@ -1312,7 +1476,7 @@ func TestConstraintConstraintInvalid(t *testing.T) {
for i, d := range data { for i, d := range data {
tc := NewTestConstraint(debug) tc := NewTestConstraint(debug)
result := tc.constraintInvalid(d.fn) result := tc.constraintValid(d.fn)
msg := fmt.Sprintf("test[%d]: %+v, result: %v", i, d, result) msg := fmt.Sprintf("test[%d]: %+v, result: %v", i, d, result)
@ -1321,13 +1485,13 @@ func TestConstraintConstraintInvalid(t *testing.T) {
} }
if d.valid { if d.valid {
assert.False(result, msg) assert.True(result, msg)
if len(d.expected.Passed) != 0 { if len(d.expected.Passed) != 0 {
assert.Equal(d.expected.Passed[0].Success, tc.Passed[0].Success, msg) assert.Equal(d.expected.Passed[0].Success, tc.Passed[0].Success, msg)
} }
} else { } else {
assert.True(result, msg) assert.False(result, msg)
if len(d.expected.Failed) != 0 { if len(d.expected.Failed) != 0 {
assert.Equal(d.expected.Failed[0].Success, tc.Failed[0].Success, msg) assert.Equal(d.expected.Failed[0].Success, tc.Failed[0].Success, msg)