diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:25:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:25:22 +0000 |
commit | f6ad4dcef54c5ce997a4bad5a6d86de229015700 (patch) | |
tree | 7cfa4e31ace5c2bd95c72b154d15af494b2bcbef /src/cmd/compile/internal/rangefunc/rangefunc_test.go | |
parent | Initial commit. (diff) | |
download | golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.tar.xz golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.zip |
Adding upstream version 1.22.1.upstream/1.22.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/compile/internal/rangefunc/rangefunc_test.go')
-rw-r--r-- | src/cmd/compile/internal/rangefunc/rangefunc_test.go | 1297 |
1 files changed, 1297 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/rangefunc/rangefunc_test.go b/src/cmd/compile/internal/rangefunc/rangefunc_test.go new file mode 100644 index 0000000..16856c6 --- /dev/null +++ b/src/cmd/compile/internal/rangefunc/rangefunc_test.go @@ -0,0 +1,1297 @@ +// Copyright 2023 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. + +//go:build goexperiment.rangefunc + +package rangefunc_test + +import ( + "slices" + "testing" +) + +type Seq2[T1, T2 any] func(yield func(T1, T2) bool) + +// OfSliceIndex returns a Seq over the elements of s. It is equivalent +// to range s. +func OfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] { + return func(yield func(int, T) bool) { + for i, v := range s { + if !yield(i, v) { + return + } + } + return + } +} + +// BadOfSliceIndex is "bad" because it ignores the return value from yield +// and just keeps on iterating. +func BadOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] { + return func(yield func(int, T) bool) { + for i, v := range s { + yield(i, v) + } + return + } +} + +// VeryBadOfSliceIndex is "very bad" because it ignores the return value from yield +// and just keeps on iterating, and also wraps that call in a defer-recover so it can +// keep on trying after the first panic. +func VeryBadOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] { + return func(yield func(int, T) bool) { + for i, v := range s { + func() { + defer func() { + recover() + }() + yield(i, v) + }() + } + return + } +} + +// CooperativeBadOfSliceIndex calls the loop body from a goroutine after +// a ping on a channel, and returns recover()on that same channel. +func CooperativeBadOfSliceIndex[T any, S ~[]T](s S, proceed chan any) Seq2[int, T] { + return func(yield func(int, T) bool) { + for i, v := range s { + if !yield(i, v) { + // if the body breaks, call yield just once in a goroutine + go func() { + <-proceed + defer func() { + proceed <- recover() + }() + yield(0, s[0]) + }() + return + } + } + return + } +} + +// TrickyIterator is a type intended to test whether an iterator that +// calls a yield function after loop exit must inevitably escape the +// closure; this might be relevant to future checking/optimization. +type TrickyIterator struct { + yield func(int, int) bool +} + +func (ti *TrickyIterator) iterAll(s []int) Seq2[int, int] { + return func(yield func(int, int) bool) { + ti.yield = yield // Save yield for future abuse + for i, v := range s { + if !yield(i, v) { + return + } + } + return + } +} + +func (ti *TrickyIterator) iterOne(s []int) Seq2[int, int] { + return func(yield func(int, int) bool) { + ti.yield = yield // Save yield for future abuse + if len(s) > 0 { // Not in a loop might escape differently + yield(0, s[0]) + } + return + } +} + +func (ti *TrickyIterator) iterZero(s []int) Seq2[int, int] { + return func(yield func(int, int) bool) { + ti.yield = yield // Save yield for future abuse + // Don't call it at all, maybe it won't escape + return + } +} + +func (ti *TrickyIterator) fail() { + if ti.yield != nil { + ti.yield(1, 1) + } +} + +// Check wraps the function body passed to iterator forall +// in code that ensures that it cannot (successfully) be called +// either after body return false (control flow out of loop) or +// forall itself returns (the iteration is now done). +// +// Note that this can catch errors before the inserted checks. +func Check[U, V any](forall Seq2[U, V]) Seq2[U, V] { + return func(body func(U, V) bool) { + ret := true + forall(func(u U, v V) bool { + if !ret { + panic("Checked iterator access after exit") + } + ret = body(u, v) + return ret + }) + ret = false + } +} + +func TestCheck(t *testing.T) { + i := 0 + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + } else { + t.Error("Wanted to see a failure") + } + }() + for _, x := range Check(BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})) { + i += x + if i > 4*9 { + break + } + } +} + +func TestCooperativeBadOfSliceIndex(t *testing.T) { + i := 0 + proceed := make(chan any) + for _, x := range CooperativeBadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, proceed) { + i += x + if i >= 36 { + break + } + } + proceed <- true + if r := <-proceed; r != nil { + t.Logf("Saw expected panic '%v'", r) + } else { + t.Error("Wanted to see a failure") + } + if i != 36 { + t.Errorf("Expected i == 36, saw %d instead", i) + } else { + t.Logf("i = %d", i) + } +} + +func TestCheckCooperativeBadOfSliceIndex(t *testing.T) { + i := 0 + proceed := make(chan any) + for _, x := range Check(CooperativeBadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, proceed)) { + i += x + if i >= 36 { + break + } + } + proceed <- true + if r := <-proceed; r != nil { + t.Logf("Saw expected panic '%v'", r) + } else { + t.Error("Wanted to see a failure") + } + if i != 36 { + t.Errorf("Expected i == 36, saw %d instead", i) + } else { + t.Logf("i = %d", i) + } +} + +func TestTrickyIterAll(t *testing.T) { + trickItAll := TrickyIterator{} + i := 0 + for _, x := range trickItAll.iterAll([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + i += x + if i >= 36 { + break + } + } + + if i != 36 { + t.Errorf("Expected i == 36, saw %d instead", i) + } else { + t.Logf("i = %d", i) + } + + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + } else { + t.Error("Wanted to see a failure") + } + }() + + trickItAll.fail() +} + +func TestTrickyIterOne(t *testing.T) { + trickItOne := TrickyIterator{} + i := 0 + for _, x := range trickItOne.iterOne([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + i += x + if i >= 36 { + break + } + } + + // Don't care about value, ought to be 36 anyhow. + t.Logf("i = %d", i) + + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + } else { + t.Error("Wanted to see a failure") + } + }() + + trickItOne.fail() +} + +func TestTrickyIterZero(t *testing.T) { + trickItZero := TrickyIterator{} + i := 0 + for _, x := range trickItZero.iterZero([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + i += x + if i >= 36 { + break + } + } + + // Don't care about value, ought to be 0 anyhow. + t.Logf("i = %d", i) + + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + } else { + t.Error("Wanted to see a failure") + } + }() + + trickItZero.fail() +} + +func TestCheckTrickyIterZero(t *testing.T) { + trickItZero := TrickyIterator{} + i := 0 + for _, x := range Check(trickItZero.iterZero([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})) { + i += x + if i >= 36 { + break + } + } + + // Don't care about value, ought to be 0 anyhow. + t.Logf("i = %d", i) + + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + } else { + t.Error("Wanted to see a failure") + } + }() + + trickItZero.fail() +} + +// TestBreak1 should just work, with well-behaved iterators. +// (The misbehaving iterator detector should not trigger.) +func TestBreak1(t *testing.T) { + var result []int + var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3} + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4}) { + if x == -4 { + break + } + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + break + } + result = append(result, y) + } + result = append(result, x) + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestBreak2 should just work, with well-behaved iterators. +// (The misbehaving iterator detector should not trigger.) +func TestBreak2(t *testing.T) { + var result []int + var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3} +outer: + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4}) { + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + break + } + if x == -4 { + break outer + } + + result = append(result, y) + } + result = append(result, x) + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestContinue should just work, with well-behaved iterators. +// (The misbehaving iterator detector should not trigger.) +func TestContinue(t *testing.T) { + var result []int + var expect = []int{-1, 1, 2, -2, 1, 2, -3, 1, 2, -4} +outer: + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4}) { + result = append(result, x) + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + continue outer + } + if x == -4 { + break outer + } + + result = append(result, y) + } + result = append(result, x-10) + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestBreak3 should just work, with well-behaved iterators. +// (The misbehaving iterator detector should not trigger.) +func TestBreak3(t *testing.T) { + var result []int + var expect = []int{100, 10, 2, 4, 200, 10, 2, 4, 20, 2, 4, 300, 10, 2, 4, 20, 2, 4, 30} +X: + for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) { + Y: + for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) { + if 10*y >= x { + break + } + result = append(result, y) + if y == 30 { + continue X + } + Z: + for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue Z + } + result = append(result, z) + if z >= 4 { + continue Y + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestBreak1BadA should end in a panic when the outer-loop's +// single-level break is ignore by BadOfSliceIndex +func TestBreak1BadA(t *testing.T) { + var result []int + var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3} + + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Error("Wanted to see a failure") + } + }() + + for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) { + if x == -4 { + break + } + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + break + } + result = append(result, y) + } + result = append(result, x) + } +} + +// TestBreak1BadB should end in a panic, sooner, when the inner-loop's +// (nested) single-level break is ignored by BadOfSliceIndex +func TestBreak1BadB(t *testing.T) { + var result []int + var expect = []int{1, 2} // inner breaks, panics, after before outer appends + + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Error("Wanted to see a failure") + } + }() + + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) { + if x == -4 { + break + } + for _, y := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + break + } + result = append(result, y) + } + result = append(result, x) + } +} + +// TestMultiCont0 tests multilevel continue with no bad iterators +// (it should just work) +func TestMultiCont0(t *testing.T) { + var result []int + var expect = []int{1000, 10, 2, 4, 2000} + +W: + for _, w := range OfSliceIndex([]int{1000, 2000}) { + result = append(result, w) + if w == 2000 { + break + } + for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) { + for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) { + result = append(result, y) + for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue + } + result = append(result, z) + if z >= 4 { + continue W // modified to be multilevel + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestMultiCont1 tests multilevel continue with a bad iterator +// in the outermost loop exited by the continue. +func TestMultiCont1(t *testing.T) { + var result []int + var expect = []int{1000, 10, 2, 4} + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Errorf("Wanted to see a failure, result was %v", result) + } + }() + +W: + for _, w := range OfSliceIndex([]int{1000, 2000}) { + result = append(result, w) + if w == 2000 { + break + } + for _, x := range BadOfSliceIndex([]int{100, 200, 300, 400}) { + for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) { + result = append(result, y) + for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue + } + result = append(result, z) + if z >= 4 { + continue W + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestMultiCont2 tests multilevel continue with a bad iterator +// in a middle loop exited by the continue. +func TestMultiCont2(t *testing.T) { + var result []int + var expect = []int{1000, 10, 2, 4} + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Errorf("Wanted to see a failure, result was %v", result) + } + }() + +W: + for _, w := range OfSliceIndex([]int{1000, 2000}) { + result = append(result, w) + if w == 2000 { + break + } + for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) { + for _, y := range BadOfSliceIndex([]int{10, 20, 30, 40}) { + result = append(result, y) + for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue + } + result = append(result, z) + if z >= 4 { + continue W + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestMultiCont3 tests multilevel continue with a bad iterator +// in the innermost loop exited by the continue. +func TestMultiCont3(t *testing.T) { + var result []int + var expect = []int{1000, 10, 2, 4} + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Errorf("Wanted to see a failure, result was %v", result) + } + }() + +W: + for _, w := range OfSliceIndex([]int{1000, 2000}) { + result = append(result, w) + if w == 2000 { + break + } + for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) { + for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) { + result = append(result, y) + for _, z := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue + } + result = append(result, z) + if z >= 4 { + continue W + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestMultiBreak0 tests multilevel break with a bad iterator +// in the outermost loop exited by the break (the outermost loop). +func TestMultiBreak0(t *testing.T) { + var result []int + var expect = []int{1000, 10, 2, 4} + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Errorf("Wanted to see a failure, result was %v", result) + } + }() + +W: + for _, w := range BadOfSliceIndex([]int{1000, 2000}) { + result = append(result, w) + if w == 2000 { + break + } + for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) { + for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) { + result = append(result, y) + for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue + } + result = append(result, z) + if z >= 4 { + break W + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestMultiBreak1 tests multilevel break with a bad iterator +// in an intermediate loop exited by the break. +func TestMultiBreak1(t *testing.T) { + var result []int + var expect = []int{1000, 10, 2, 4} + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Errorf("Wanted to see a failure, result was %v", result) + } + }() + +W: + for _, w := range OfSliceIndex([]int{1000, 2000}) { + result = append(result, w) + if w == 2000 { + break + } + for _, x := range BadOfSliceIndex([]int{100, 200, 300, 400}) { + for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) { + result = append(result, y) + for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue + } + result = append(result, z) + if z >= 4 { + break W + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestMultiBreak2 tests multilevel break with two bad iterators +// in intermediate loops exited by the break. +func TestMultiBreak2(t *testing.T) { + var result []int + var expect = []int{1000, 10, 2, 4} + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Errorf("Wanted to see a failure, result was %v", result) + } + }() + +W: + for _, w := range OfSliceIndex([]int{1000, 2000}) { + result = append(result, w) + if w == 2000 { + break + } + for _, x := range BadOfSliceIndex([]int{100, 200, 300, 400}) { + for _, y := range BadOfSliceIndex([]int{10, 20, 30, 40}) { + result = append(result, y) + for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue + } + result = append(result, z) + if z >= 4 { + break W + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestMultiBreak3 tests multilevel break with the bad iterator +// in the innermost loop exited by the break. +func TestMultiBreak3(t *testing.T) { + var result []int + var expect = []int{1000, 10, 2, 4} + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + } else { + t.Errorf("Wanted to see a failure, result was %v", result) + } + }() + +W: + for _, w := range OfSliceIndex([]int{1000, 2000}) { + result = append(result, w) + if w == 2000 { + break + } + for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) { + for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) { + result = append(result, y) + for _, z := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if z&1 == 1 { + continue + } + result = append(result, z) + if z >= 4 { + break W + } + } + result = append(result, -y) // should never be executed + } + result = append(result, x) + } + } + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// veryBad tests that a loop nest behaves sensibly in the face of a +// "very bad" iterator. In this case, "sensibly" means that the +// break out of X still occurs after the very bad iterator finally +// quits running (the control flow bread crumbs remain.) +func veryBad(s []int) []int { + var result []int +X: + for _, x := range OfSliceIndex([]int{1, 2, 3}) { + + result = append(result, x) + + for _, y := range VeryBadOfSliceIndex(s) { + result = append(result, y) + break X + } + for _, z := range OfSliceIndex([]int{100, 200, 300}) { + result = append(result, z) + if z == 100 { + break + } + } + } + return result +} + +// checkVeryBad wraps a "very bad" iterator with Check, +// demonstrating that the very bad iterator also hides panics +// thrown by Check. +func checkVeryBad(s []int) []int { + var result []int +X: + for _, x := range OfSliceIndex([]int{1, 2, 3}) { + + result = append(result, x) + + for _, y := range Check(VeryBadOfSliceIndex(s)) { + result = append(result, y) + break X + } + for _, z := range OfSliceIndex([]int{100, 200, 300}) { + result = append(result, z) + if z == 100 { + break + } + } + } + return result +} + +// okay is the not-bad version of veryBad. +// They should behave the same. +func okay(s []int) []int { + var result []int +X: + for _, x := range OfSliceIndex([]int{1, 2, 3}) { + + result = append(result, x) + + for _, y := range OfSliceIndex(s) { + result = append(result, y) + break X + } + for _, z := range OfSliceIndex([]int{100, 200, 300}) { + result = append(result, z) + if z == 100 { + break + } + } + } + return result +} + +// TestVeryBad1 checks the behavior of an extremely poorly behaved iterator. +func TestVeryBad1(t *testing.T) { + result := veryBad([]int{10, 20, 30, 40, 50}) // odd length + expect := []int{1, 10} + + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestVeryBad2 checks the behavior of an extremely poorly behaved iterator. +func TestVeryBad2(t *testing.T) { + result := veryBad([]int{10, 20, 30, 40}) // even length + expect := []int{1, 10} + + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestCheckVeryBad checks the behavior of an extremely poorly behaved iterator, +// which also suppresses the exceptions from "Check" +func TestCheckVeryBad(t *testing.T) { + result := checkVeryBad([]int{10, 20, 30, 40}) // even length + expect := []int{1, 10} + + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// TestOk is the nice version of the very bad iterator. +func TestOk(t *testing.T) { + result := okay([]int{10, 20, 30, 40, 50}) // odd length + expect := []int{1, 10} + + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } +} + +// testBreak1BadDefer checks that defer behaves properly even in +// the presence of loop bodies panicking out of bad iterators. +// (i.e., the instrumentation did not break defer in these loops) +func testBreak1BadDefer(t *testing.T) (result []int) { + var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3, -30, -20, -10} + + defer func() { + if r := recover(); r != nil { + t.Logf("Saw expected panic '%v'", r) + if !slices.Equal(expect, result) { + t.Errorf("(Inner) Expected %v, got %v", expect, result) + } + } else { + t.Error("Wanted to see a failure") + } + }() + + for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) { + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + break + } + result = append(result, y) + } + result = append(result, x) + } + return +} + +func TestBreak1BadDefer(t *testing.T) { + var result []int + var expect = []int{1, 2, -1, 1, 2, -2, 1, 2, -3, -30, -20, -10} + result = testBreak1BadDefer(t) + if !slices.Equal(expect, result) { + t.Errorf("(Outer) Expected %v, got %v", expect, result) + } +} + +// testReturn1 has no bad iterators. +func testReturn1(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + return + } + result = append(result, y) + } + result = append(result, x) + } + return +} + +// testReturn2 has an outermost bad iterator +func testReturn2(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + return + } + result = append(result, y) + } + result = append(result, x) + } + return +} + +// testReturn3 has an innermost bad iterator +func testReturn3(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + return + } + result = append(result, y) + } + } + return +} + +// TestReturns checks that returns through bad iterators behave properly, +// for inner and outer bad iterators. +func TestReturns(t *testing.T) { + var result []int + var expect = []int{-1, 1, 2, -10} + var err any + + result, err = testReturn1(t) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + if err != nil { + t.Errorf("Unexpected error %v", err) + } + + result, err = testReturn2(t) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + if err == nil { + t.Errorf("Missing expected error") + } else { + t.Logf("Saw expected panic '%v'", err) + } + + result, err = testReturn3(t) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + if err == nil { + t.Errorf("Missing expected error") + } else { + t.Logf("Saw expected panic '%v'", err) + } + +} + +// testGotoA1 tests loop-nest-internal goto, no bad iterators. +func testGotoA1(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + goto A + } + result = append(result, y) + } + result = append(result, x) + A: + } + return +} + +// testGotoA2 tests loop-nest-internal goto, outer bad iterator. +func testGotoA2(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + goto A + } + result = append(result, y) + } + result = append(result, x) + A: + } + return +} + +// testGotoA3 tests loop-nest-internal goto, inner bad iterator. +func testGotoA3(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + goto A + } + result = append(result, y) + } + result = append(result, x) + A: + } + return +} + +func TestGotoA(t *testing.T) { + var result []int + var expect = []int{-1, 1, 2, -2, 1, 2, -3, 1, 2, -4, -30, -20, -10} + var expect3 = []int{-1, 1, 2, -10} // first goto becomes a panic + var err any + + result, err = testGotoA1(t) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + if err != nil { + t.Errorf("Unexpected error %v", err) + } + + result, err = testGotoA2(t) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + if err == nil { + t.Errorf("Missing expected error") + } else { + t.Logf("Saw expected panic '%v'", err) + } + + result, err = testGotoA3(t) + if !slices.Equal(expect3, result) { + t.Errorf("Expected %v, got %v", expect3, result) + } + if err == nil { + t.Errorf("Missing expected error") + } else { + t.Logf("Saw expected panic '%v'", err) + } +} + +// testGotoB1 tests loop-nest-exiting goto, no bad iterators. +func testGotoB1(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + goto B + } + result = append(result, y) + } + result = append(result, x) + } +B: + result = append(result, 999) + return +} + +// testGotoB2 tests loop-nest-exiting goto, outer bad iterator. +func testGotoB2(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range BadOfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + goto B + } + result = append(result, y) + } + result = append(result, x) + } +B: + result = append(result, 999) + return +} + +// testGotoB3 tests loop-nest-exiting goto, inner bad iterator. +func testGotoB3(t *testing.T) (result []int, err any) { + defer func() { + err = recover() + }() + for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) { + result = append(result, x) + if x == -4 { + break + } + defer func() { + result = append(result, x*10) + }() + for _, y := range BadOfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + if y == 3 { + goto B + } + result = append(result, y) + } + result = append(result, x) + } +B: + result = append(result, 999) + return +} + +func TestGotoB(t *testing.T) { + var result []int + var expect = []int{-1, 1, 2, 999, -10} + var expectX = []int{-1, 1, 2, -10} + var err any + + result, err = testGotoB1(t) + if !slices.Equal(expect, result) { + t.Errorf("Expected %v, got %v", expect, result) + } + if err != nil { + t.Errorf("Unexpected error %v", err) + } + + result, err = testGotoB2(t) + if !slices.Equal(expectX, result) { + t.Errorf("Expected %v, got %v", expectX, result) + } + if err == nil { + t.Errorf("Missing expected error") + } else { + t.Logf("Saw expected panic '%v'", err) + } + + result, err = testGotoB3(t) + if !slices.Equal(expectX, result) { + t.Errorf("Expected %v, got %v", expectX, result) + } + if err == nil { + t.Errorf("Missing expected error") + } else { + t.Logf("Saw expected panic '%v'", err) + } +} |