// Copyright (c) 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package field import ( "testing" "testing/quick" ) func checkAliasingOneArg(f func(v, x *Element) *Element) func(v, x Element) bool { return func(v, x Element) bool { x1, v1 := x, x // Calculate a reference f(x) without aliasing. if out := f(&v, &x); out != &v && isInBounds(out) { return false } // Test aliasing the argument and the receiver. if out := f(&v1, &v1); out != &v1 || v1 != v { return false } // Ensure the arguments was not modified. return x == x1 } } func checkAliasingTwoArgs(f func(v, x, y *Element) *Element) func(v, x, y Element) bool { return func(v, x, y Element) bool { x1, y1, v1 := x, y, Element{} // Calculate a reference f(x, y) without aliasing. if out := f(&v, &x, &y); out != &v && isInBounds(out) { return false } // Test aliasing the first argument and the receiver. v1 = x if out := f(&v1, &v1, &y); out != &v1 || v1 != v { return false } // Test aliasing the second argument and the receiver. v1 = y if out := f(&v1, &x, &v1); out != &v1 || v1 != v { return false } // Calculate a reference f(x, x) without aliasing. if out := f(&v, &x, &x); out != &v { return false } // Test aliasing the first argument and the receiver. v1 = x if out := f(&v1, &v1, &x); out != &v1 || v1 != v { return false } // Test aliasing the second argument and the receiver. v1 = x if out := f(&v1, &x, &v1); out != &v1 || v1 != v { return false } // Test aliasing both arguments and the receiver. v1 = x if out := f(&v1, &v1, &v1); out != &v1 || v1 != v { return false } // Ensure the arguments were not modified. return x == x1 && y == y1 } } // TestAliasing checks that receivers and arguments can alias each other without // leading to incorrect results. That is, it ensures that it's safe to write // // v.Invert(v) // // or // // v.Add(v, v) // // without any of the inputs getting clobbered by the output being written. func TestAliasing(t *testing.T) { type target struct { name string oneArgF func(v, x *Element) *Element twoArgsF func(v, x, y *Element) *Element } for _, tt := range []target{ {name: "Absolute", oneArgF: (*Element).Absolute}, {name: "Invert", oneArgF: (*Element).Invert}, {name: "Negate", oneArgF: (*Element).Negate}, {name: "Set", oneArgF: (*Element).Set}, {name: "Square", oneArgF: (*Element).Square}, {name: "Multiply", twoArgsF: (*Element).Multiply}, {name: "Add", twoArgsF: (*Element).Add}, {name: "Subtract", twoArgsF: (*Element).Subtract}, { name: "Select0", twoArgsF: func(v, x, y *Element) *Element { return (*Element).Select(v, x, y, 0) }, }, { name: "Select1", twoArgsF: func(v, x, y *Element) *Element { return (*Element).Select(v, x, y, 1) }, }, } { var err error switch { case tt.oneArgF != nil: err = quick.Check(checkAliasingOneArg(tt.oneArgF), &quick.Config{MaxCountScale: 1 << 8}) case tt.twoArgsF != nil: err = quick.Check(checkAliasingTwoArgs(tt.twoArgsF), &quick.Config{MaxCountScale: 1 << 8}) } if err != nil { t.Errorf("%v: %v", tt.name, err) } } }