// Copyright 2013 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.

// This program is processed by the cover command, and then testAll is called.
// The test driver in main.go can then compare the coverage statistics with expectation.

// The word LINE is replaced by the line number in this file. When the file is executed,
// the coverage processing has changed the line numbers, so we can't use runtime.Caller.

package main

const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often"

func testAll() {
	testSimple()
	testBlockRun()
	testIf()
	testFor()
	testRange()
	testSwitch()
	testTypeSwitch()
	testSelect1()
	testSelect2()
	testPanic()
	testEmptySwitches()
	testFunctionLiteral()
}

// The indexes of the counters in testPanic are known to main.go
const panicIndex = 3

// This test appears first because the index of its counters is known to main.go
func testPanic() {
	defer func() {
		recover()
	}()
	check(LINE, 1)
	panic("should not get next line")
	check(LINE, 0) // this is GoCover.Count[panicIndex]
	// The next counter is in testSimple and it will be non-zero.
	// If the panic above does not trigger a counter, the test will fail
	// because GoCover.Count[panicIndex] will be the one in testSimple.
}

func testSimple() {
	check(LINE, 1)
}

func testIf() {
	if true {
		check(LINE, 1)
	} else {
		check(LINE, 0)
	}
	if false {
		check(LINE, 0)
	} else {
		check(LINE, 1)
	}
	for i := 0; i < 3; i++ {
		if checkVal(LINE, 3, i) <= 2 {
			check(LINE, 3)
		}
		if checkVal(LINE, 3, i) <= 1 {
			check(LINE, 2)
		}
		if checkVal(LINE, 3, i) <= 0 {
			check(LINE, 1)
		}
	}
	for i := 0; i < 3; i++ {
		if checkVal(LINE, 3, i) <= 1 {
			check(LINE, 2)
		} else {
			check(LINE, 1)
		}
	}
	for i := 0; i < 3; i++ {
		if checkVal(LINE, 3, i) <= 0 {
			check(LINE, 1)
		} else if checkVal(LINE, 2, i) <= 1 {
			check(LINE, 1)
		} else if checkVal(LINE, 1, i) <= 2 {
			check(LINE, 1)
		} else if checkVal(LINE, 0, i) <= 3 {
			check(LINE, 0)
		}
	}
	if func(a, b int) bool { return a < b }(3, 4) {
		check(LINE, 1)
	}
}

func testFor() {
	for i := 0; i < 10; func() { i++; check(LINE, 10) }() {
		check(LINE, 10)
	}
}

func testRange() {
	for _, f := range []func(){
		func() { check(LINE, 1) },
	} {
		f()
		check(LINE, 1)
	}
}

func testBlockRun() {
	check(LINE, 1)
	{
		check(LINE, 1)
	}
	{
		check(LINE, 1)
	}
	check(LINE, 1)
	{
		check(LINE, 1)
	}
	{
		check(LINE, 1)
	}
	check(LINE, 1)
}

func testSwitch() {
	for i := 0; i < 5; func() { i++; check(LINE, 5) }() {
		switch i {
		case 0:
			check(LINE, 1)
		case 1:
			check(LINE, 1)
		case 2:
			check(LINE, 1)
		default:
			check(LINE, 2)
		}
	}
}

func testTypeSwitch() {
	var x = []interface{}{1, 2.0, "hi"}
	for _, v := range x {
		switch func() { check(LINE, 3) }(); v.(type) {
		case int:
			check(LINE, 1)
		case float64:
			check(LINE, 1)
		case string:
			check(LINE, 1)
		case complex128:
			check(LINE, 0)
		default:
			check(LINE, 0)
		}
	}
}

func testSelect1() {
	c := make(chan int)
	go func() {
		for i := 0; i < 1000; i++ {
			c <- i
		}
	}()
	for {
		select {
		case <-c:
			check(LINE, anything)
		case <-c:
			check(LINE, anything)
		default:
			check(LINE, 1)
			return
		}
	}
}

func testSelect2() {
	c1 := make(chan int, 1000)
	c2 := make(chan int, 1000)
	for i := 0; i < 1000; i++ {
		c1 <- i
		c2 <- i
	}
	for {
		select {
		case <-c1:
			check(LINE, 1000)
		case <-c2:
			check(LINE, 1000)
		default:
			check(LINE, 1)
			return
		}
	}
}

// Empty control statements created syntax errors. This function
// is here just to be sure that those are handled correctly now.
func testEmptySwitches() {
	check(LINE, 1)
	switch 3 {
	}
	check(LINE, 1)
	switch i := (interface{})(3).(int); i {
	}
	check(LINE, 1)
	c := make(chan int)
	go func() {
		check(LINE, 1)
		c <- 1
		select {}
	}()
	<-c
	check(LINE, 1)
}

func testFunctionLiteral() {
	a := func(f func()) error {
		f()
		f()
		return nil
	}

	b := func(f func()) bool {
		f()
		f()
		return true
	}

	check(LINE, 1)
	a(func() {
		check(LINE, 2)
	})

	if err := a(func() {
		check(LINE, 2)
	}); err != nil {
	}

	switch b(func() {
		check(LINE, 2)
	}) {
	}
}
