# Note that this is the only test with a module where we rely on the detection
# of GOPRIVATE.
# Also note that, since this is the only test using "real" external modules
# fetched via GOPROXY, go.mod and go.sum should declare the dependencies.

# For now, use a throwaway module download cache instead of the host machine's.
# Usually it would be fine to reuse the host's, since we expose exact copies of
# some external modules in a local proxy, allowing 'go test' to work offline.
# However, for some reason, we end up with different hashes of the code for
# modules like rsc.io/quote, and it's unclear why. It might be a txtar-addmod bug.
# In any case, not worth our time to investigate right now, and "downloading"
# modules is instant since we just copy a handful of files.
#
# To reproduce the issue, remove the env line and run:
#
#    go clean -modcache && go get -d rsc.io/quote@v1.5.2 && go test -short
env GOMODCACHE=$WORK/modcache

garble build -tags buildtag
exec ./main
cmp stdout main.stdout

! binsubstr main$exe 'ImportedVar' 'ImportedConst' 'ImportedFunc' 'ImportedType' 'main.go' 'test/main' 'importedpkg.' 'NormalStruct' 'NormalExportedField' 'normalUnexportedField'
binsubstr main$exe 'ReflectInDefined' 'ExportedField2' 'unexportedField2'

[short] stop # checking that the build is reproducible is slow

# Also check that the binary is reproducible when many imports are involved.
# No packages should be rebuilt either, thanks to the build cache.
cp main$exe main_old$exe
rm main$exe
garble build -tags buildtag -v
! stderr .
bincmp main$exe main_old$exe

go build -tags buildtag
exec ./main
cmp stdout main.stdout

# Also check that -literals doesn't break anything
garble -literals build -tags buildtag
exec ./main
cmp stdout main.stdout

-- go.mod --
module test/main

go 1.16

require (
	rsc.io/quote v1.5.2
	gopkg.in/garbletest.v2 v2.999.0
)
-- go.sum --
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/garbletest.v2 v2.999.0 h1:wiZfOKGiXX7DoYVgbNvnTaCjqElrpZQSvKg0HYouw/o=
gopkg.in/garbletest.v2 v2.999.0/go.mod h1:MF1BPTBjmDdc9x86+9UMLL9pAH2eMFPHvltohOvlGEw=
rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-- main.go --
package main

import (
	"fmt"
	"reflect"
	"strings"

	"test/main/importedpkg"

	"rsc.io/quote"
	garbletest "gopkg.in/garbletest.v2"
)

func main() {
	fmt.Println(importedpkg.ImportedVar)
	fmt.Println(importedpkg.ImportedConst)
	fmt.Println(importedpkg.ImportedFunc('x'))
	fmt.Println(importedpkg.ImportedType(3))
	fmt.Println(importedpkg.ReflectInDefinedVar.ExportedField2)
	fmt.Println(importedpkg.ReflectInDefined{ExportedField2: 5})
	normal := importedpkg.NormalStruct{SharedName: 3}
	normal.IndirectStruct.Field = 23
	fmt.Println(normal)
	printfWithoutPackage("%T\n", importedpkg.ReflectTypeOf(2))
	printfWithoutPackage("%T\n", importedpkg.ReflectTypeOfIndirect(4))

	v := importedpkg.ReflectValueOfVar
	printfWithoutPackage("%#v\n", v)
	method := reflect.ValueOf(&v).MethodByName("ExportedMethodName")
	if method.IsValid() {
		fmt.Println(method.Call(nil))
	} else {
		fmt.Println("method not found")
	}

	fmt.Println(quote.Go())
	garbletest.Test()
}

func printfWithoutPackage(format string, v interface{}) {
	fmt.Print(strings.Split(fmt.Sprintf(format, v), ".")[1])
}
-- notag_fail.go --
// +build !buildtag

package main

var foo int = "should be omitted by -tags"
-- withtag_success.go --
// +build buildtag

package main

import "fmt"

func init() { fmt.Println("buildtag init func") }
-- differentpkg_unnamed.go --
package main

import "test/main/different-pkg-name"

var _ = actualpkgname.Noop
-- differentpkg_named.go --
package main

import named "test/main/different-pkg-name"

var _ = named.Noop
-- importedpkg/imported.go --
package importedpkg

import (
	"reflect"
	"test/main/importedpkg/indirect"
)

var ImportedVar = "imported var value"

const ImportedConst = "imported const value"

func ImportedFunc(param rune) string {
	return string(param)
}

type ReflectTypeOf int

var _ = reflect.TypeOf(ReflectTypeOf(0))

type ReflectTypeOfIndirect int

var _ = reflect.TypeOf(new([]*ReflectTypeOfIndirect))

type ReflectValueOf struct {
	ExportedField string

	unexportedField string
}

func (r *ReflectValueOf) ExportedMethodName() string { return "method: " + r.ExportedField }

var ReflectValueOfVar = ReflectValueOf{ExportedField: "abc"}

var _ = reflect.TypeOf(ReflectValueOfVar)

type ReflectInDefined struct {
	ExportedField2 int

	unexportedField2 int
}

var ReflectInDefinedVar = ReflectInDefined{ExportedField2: 9000}

var _ = reflect.TypeOf(ReflectInDefinedVar)

const SharedName = 2

type NormalStruct struct {
	SharedName            int
	IndirectStruct        indirect.Indirect
	normalUnexportedField int
}

// ImportedType comes after the calls to reflect, to ensure no false positives.
type ImportedType int

-- importedpkg/indirect/indirect.go --
package indirect

type Indirect struct {
	Field int
}

-- different-pkg-name/pkg.go --
package actualpkgname

var Noop int

-- main.stdout --
buildtag init func
imported var value
imported const value
x
3
9000
{5 0}
{3 {23} 0}
ReflectTypeOf
ReflectTypeOfIndirect
ReflectValueOf{ExportedField:"abc", unexportedField:""}
[method: abc]
Don't communicate by sharing memory, share memory by communicating.
