Sort

func Sort(how any, expectedValue any) TestDeep

Sort is a smuggler operator. It takes an array, a slice or a pointer on array/slice, it sorts it using how and compares the sorted result to expectedValue. It can be seen as an alternative to Bag.

how can be:

  • nil or a float64/int >= 0 for a generic ascending order;
  • a float64/int < 0 for a generic descending order;
  • a string specifying a fields-path (optionally prefixed by “+” or “-” for respectively an ascending or a descending order, defaulting to ascending one);
  • a []string containing a list of fields-paths (as above), second and next fields-paths are checked when the previous ones are equal;
  • a function matching func(a, b T) bool signature and returning true if a is before b.

A fields-path, also used by Smuggle and Sorted operators, allows to access nested structs fields and maps & slices items. See Smuggle for details on fields-path possibilities.

type A struct{ props map[string]int }
p12 := A{props: map[string]int{"priority": 12}}
p23 := A{props: map[string]int{"priority": 23}}
p34 := A{props: map[string]int{"priority": 34}}
got := []A{p23, p12, p34}
td.Cmp(t, got, td.Sort("-props[priority]", []A{p34, p23, p12})) // succeeds

how can be a float64 to allow Sort to be used in expected JSON of JSON, SubJSONOf & SuperJSONOf operators:

got := map[string][]string{"labels": {"c", "a", "b"}}
td.Cmp(t, got, td.JSON(`{ "labels": Sort(1, ["a", "b", "c"]) }`)) // succeeds

or using fields-path feature:

type Person struct {
  Name string `json:"name"`
  Age  int    `json:"age"`
}
got := struct {
  People []Person `json:"people"`
}{
  People: []Person{
    {"Brian", 22},
    {"Bob", 19},
    {"Stephen", 19},
    {"Alice", 20},
    {"Marcel", 25},
  },
}
td.Cmp(t, got, td.JSON(`{
  "people": Sort("name", [ // sort by name ascending
    {"name": "Alice",   "age": 20},
    {"name": "Bob",     "age": 19},
    {"name": "Brian",   "age": 22},
    {"name": "Marcel",  "age": 25},
    {"name": "Stephen", "age": 19},
  ])
}`)) // succeeds
td.Cmp(t, got, td.JSON(`{
  "people": Sort([ "-age", "name" ], [ // sort by age desc, then by name asc
    {"name": "Marcel",  "age": 25},
    {"name": "Brian",   "age": 22},
    {"name": "Alice",   "age": 20},
    {"name": "Bob",     "age": 19},
    {"name": "Stephen", "age": 19},
  ])
}`)) // succeeds

See also Sorted, Smuggle and Bag.

See also Sort godoc.

Examples

Basic example
	t := &testing.T{}

	got := []int{-1, 1, 2, -3, 3, -2, 0}

	// Generic ascending order (≥0 or nil)
	ok := td.Cmp(t, got, td.Sort(1, []int{-3, -2, -1, 0, 1, 2, 3}))
	fmt.Println("asc order:", ok)

	ok = td.Cmp(t, got, td.Sort(0, []int{-3, -2, -1, 0, 1, 2, 3}))
	fmt.Println("asc order:", ok)

	ok = td.Cmp(t, got, td.Sort(nil, []int{-3, -2, -1, 0, 1, 2, 3}))
	fmt.Println("asc order:", ok)

	// Generic descending order (< 0)
	ok = td.Cmp(t, got, td.Sort(-1, []int{3, 2, 1, 0, -1, -2, -3}))
	fmt.Println("desc order:", ok)

	evenHigher := func(a, b int) bool {
		if (a%2 == 0) != (b%2 == 0) {
			return a%2 != 0
		}
		return a < b
	}
	ok = td.Cmp(t, got, td.Sort(evenHigher, []int{-3, -1, 1, 3, -2, 0, 2}))
	fmt.Println("even higher order:", ok)

	// Output:
	// asc order: true
	// asc order: true
	// asc order: true
	// desc order: true
	// even higher order: true
Fields_path example
	t := &testing.T{}

	type Person struct {
		Name string
		Age  int
	}

	brian := Person{Name: "Brian", Age: 22}
	bob := Person{Name: "Bob", Age: 19}
	stephen := Person{Name: "Stephen", Age: 19}
	alice := Person{Name: "Alice", Age: 20}
	marcel := Person{Name: "Marcel", Age: 25}
	got := []Person{brian, bob, stephen, alice, marcel}

	ok := td.Cmp(t, got,
		td.Sort("Name", []Person{alice, bob, brian, marcel, stephen}))
	fmt.Println("by name asc:", ok)

	ok = td.Cmp(t, got,
		td.Sort("-Name", []Person{stephen, marcel, brian, bob, alice}))
	fmt.Println("by name desc:", ok)

	ok = td.Cmp(t, got,
		td.Sort([]string{"-Age", "Name"}, []Person{marcel, brian, alice, bob, stephen}))
	fmt.Println("by age desc, then by name asc:", ok)

	type A struct{ props map[string]int }
	p12 := A{props: map[string]int{"priority": 12}}
	p23 := A{props: map[string]int{"priority": 23}}
	p34 := A{props: map[string]int{"priority": 34}}
	got2 := []A{p23, p12, p34}
	ok = td.Cmp(t, got2, td.Sort(`-props[priority]`, []A{p34, p23, p12}))
	fmt.Println("by priority desc:", ok)

	ok = td.Cmp(t, got2, td.Sort(`props.priority`, []A{p12, p23, p34}))
	fmt.Println("by priority asc:", ok)

	// Output:
	// by name asc: true
	// by name desc: true
	// by age desc, then by name asc: true
	// by priority desc: true
	// by priority asc: true

CmpSort shortcut

func CmpSort(t TestingT, got, how , expectedValue any, args ...any) bool

CmpSort is a shortcut for:

td.Cmp(t, got, td.Sort(how, expectedValue), args...)

See above for details.

Returns true if the test is OK, false if it fails.

If t is a *T then its Config field is inherited.

args… are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a ‘%’ rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.

See also CmpSort godoc.

Examples

Basic example
	t := &testing.T{}

	got := []int{-1, 1, 2, -3, 3, -2, 0}

	// Generic ascending order (≥0 or nil)
	ok := td.CmpSort(t, got, 1, []int{-3, -2, -1, 0, 1, 2, 3})
	fmt.Println("asc order:", ok)

	ok = td.CmpSort(t, got, 0, []int{-3, -2, -1, 0, 1, 2, 3})
	fmt.Println("asc order:", ok)

	ok = td.CmpSort(t, got, nil, []int{-3, -2, -1, 0, 1, 2, 3})
	fmt.Println("asc order:", ok)

	// Generic descending order (< 0)
	ok = td.CmpSort(t, got, -1, []int{3, 2, 1, 0, -1, -2, -3})
	fmt.Println("desc order:", ok)

	evenHigher := func(a, b int) bool {
		if (a%2 == 0) != (b%2 == 0) {
			return a%2 != 0
		}
		return a < b
	}
	ok = td.CmpSort(t, got, evenHigher, []int{-3, -1, 1, 3, -2, 0, 2})
	fmt.Println("even higher order:", ok)

	// Output:
	// asc order: true
	// asc order: true
	// asc order: true
	// desc order: true
	// even higher order: true
Fields_path example
	t := &testing.T{}

	type Person struct {
		Name string
		Age  int
	}

	brian := Person{Name: "Brian", Age: 22}
	bob := Person{Name: "Bob", Age: 19}
	stephen := Person{Name: "Stephen", Age: 19}
	alice := Person{Name: "Alice", Age: 20}
	marcel := Person{Name: "Marcel", Age: 25}
	got := []Person{brian, bob, stephen, alice, marcel}

	ok := td.CmpSort(t, got, "Name", []Person{alice, bob, brian, marcel, stephen})
	fmt.Println("by name asc:", ok)

	ok = td.CmpSort(t, got, "-Name", []Person{stephen, marcel, brian, bob, alice})
	fmt.Println("by name desc:", ok)

	ok = td.CmpSort(t, got, []string{"-Age", "Name"}, []Person{marcel, brian, alice, bob, stephen})
	fmt.Println("by age desc, then by name asc:", ok)

	type A struct{ props map[string]int }
	p12 := A{props: map[string]int{"priority": 12}}
	p23 := A{props: map[string]int{"priority": 23}}
	p34 := A{props: map[string]int{"priority": 34}}
	got2 := []A{p23, p12, p34}
	ok = td.CmpSort(t, got2, `-props[priority]`, []A{p34, p23, p12})
	fmt.Println("by priority desc:", ok)

	ok = td.CmpSort(t, got2, `props.priority`, []A{p12, p23, p34})
	fmt.Println("by priority asc:", ok)

	// Output:
	// by name asc: true
	// by name desc: true
	// by age desc, then by name asc: true
	// by priority desc: true
	// by priority asc: true

T.Sort shortcut

func (t *T) Sort(got, how , expectedValue any, args ...any) bool

Sort is a shortcut for:

t.Cmp(got, td.Sort(how, expectedValue), args...)

See above for details.

Returns true if the test is OK, false if it fails.

args… are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a ‘%’ rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.

See also T.Sort godoc.

Examples

Basic example
	t := td.NewT(&testing.T{})

	got := []int{-1, 1, 2, -3, 3, -2, 0}

	// Generic ascending order (≥0 or nil)
	ok := t.Sort(got, 1, []int{-3, -2, -1, 0, 1, 2, 3})
	fmt.Println("asc order:", ok)

	ok = t.Sort(got, 0, []int{-3, -2, -1, 0, 1, 2, 3})
	fmt.Println("asc order:", ok)

	ok = t.Sort(got, nil, []int{-3, -2, -1, 0, 1, 2, 3})
	fmt.Println("asc order:", ok)

	// Generic descending order (< 0)
	ok = t.Sort(got, -1, []int{3, 2, 1, 0, -1, -2, -3})
	fmt.Println("desc order:", ok)

	evenHigher := func(a, b int) bool {
		if (a%2 == 0) != (b%2 == 0) {
			return a%2 != 0
		}
		return a < b
	}
	ok = t.Sort(got, evenHigher, []int{-3, -1, 1, 3, -2, 0, 2})
	fmt.Println("even higher order:", ok)

	// Output:
	// asc order: true
	// asc order: true
	// asc order: true
	// desc order: true
	// even higher order: true
Fields_path example
	t := td.NewT(&testing.T{})

	type Person struct {
		Name string
		Age  int
	}

	brian := Person{Name: "Brian", Age: 22}
	bob := Person{Name: "Bob", Age: 19}
	stephen := Person{Name: "Stephen", Age: 19}
	alice := Person{Name: "Alice", Age: 20}
	marcel := Person{Name: "Marcel", Age: 25}
	got := []Person{brian, bob, stephen, alice, marcel}

	ok := t.Sort(got, "Name", []Person{alice, bob, brian, marcel, stephen})
	fmt.Println("by name asc:", ok)

	ok = t.Sort(got, "-Name", []Person{stephen, marcel, brian, bob, alice})
	fmt.Println("by name desc:", ok)

	ok = t.Sort(got, []string{"-Age", "Name"}, []Person{marcel, brian, alice, bob, stephen})
	fmt.Println("by age desc, then by name asc:", ok)

	type A struct{ props map[string]int }
	p12 := A{props: map[string]int{"priority": 12}}
	p23 := A{props: map[string]int{"priority": 23}}
	p34 := A{props: map[string]int{"priority": 34}}
	got2 := []A{p23, p12, p34}
	ok = t.Sort(got2, `-props[priority]`, []A{p34, p23, p12})
	fmt.Println("by priority desc:", ok)

	ok = t.Sort(got2, `props.priority`, []A{p12, p23, p34})
	fmt.Println("by priority asc:", ok)

	// Output:
	// by name asc: true
	// by name desc: true
	// by age desc, then by name asc: true
	// by priority desc: true
	// by priority asc: true