// Issues: #667, #695, #622
-- in.cue --
import (
	"strconv"
	"regexp"
)

// In these test, it is impossible to determine the existence of some arcs due
// to mutual dependence on becoming concrete.
//
// This tests shows the essences of when an existence check cannot be resolved.
minimal: {
	a: {
		if b.port == _|_ {
			port: ""
		}
		// Pushdown produces cycle error instead of incomplete.
		@test(err, code=cycle|incomplete)
	}

	b: {
		if a.port == _|_ {
			port: ""
		}
		// Pushdown produces cycle error instead of incomplete.
		@test(err, code=cycle|incomplete)
	}
}

small: {
	#userHostPort: #"^(:(?P<port>\d+))?$"#

	p1: {
		@test(permute, count=2)
		#Y: regexp.FindNamedSubmatch(#userHostPort, #X.port)

		#X: {
			if #Y.port == _|_ {
				port: ""
			}
			if #Y.port != _|_ {
				port: ":" + strconv.FormatInt(#Y.port, 10)
			}
		}
		// Yield-on-running heuristic: the comp's referenced node
		// #Y is not currently being processed (only scheduled), so
		// the comp does not yield and instead reports a cycle error.
		@test(eq, {
			#Y: _|_ @test(err, code=cycle|incomplete)
			#X: _|_ @test(err, code=cycle|incomplete)
		})
	}
}

medium: {
	#userHostPort: #"^(:(?P<port>\d+))?$"#

	p1: {
		@test(permute, count=6)

		#Y: regexp.FindNamedSubmatch(#userHostPort, #X.port) @test(err, code=incomplete|cycle)

		// Yield-on-running heuristic: the comp's referenced node Y
		// is not currently being processed, so the comp does not
		// yield and instead reports a cycle error.

		Y: {
			if #Y.port != _|_ {
				port: strconv.Atoi(#Y.port)
			}
		} @test(err, code=incomplete|cycle)

		#X: {
			// Can never determine whether Y.port exists as its resolution
			// depends on #Y becoming finalized, which, in turn, depends on #X
			// becoming finalized.
			if Y.port == _|_ {
				port: ""
			}
			if Y.port != _|_ {
				port: ":" + strconv.FormatInt(Y.port, 10)
			}
		} @test(err, code=incomplete|cycle)
	}
}

large: {
	#userHostPort: #"^((?P<userinfo>[[:alnum:]]*)@)?(?P<host>[[:alnum:].]+)(:(?P<port>\d+))?$"#

	p1: {
		@test(permute, count=120)
		// Pushdown improvement: #Y, #X, and X now resolve successfully.
		// Y gets a cycle error because #Y.port changed after evaluation.
		// Permute disabled: pushdown introduces ordering sensitivity.

		Y: {
			userinfo: "user"
			host:     "mod.test"
		}

		X: #X.userinfo + #X.host + #X.port @test(err, any, code=incomplete|cycle)

		#X: {
			if Y.userinfo == _|_ {
				userinfo: ""
			}
			if Y.userinfo != _|_ {
				userinfo: Y.userinfo + "@"
			}

			host: Y.host

			if Y.port == _|_ {
				port: ""
			}
			if Y.port != _|_ {
				port: ":" + strconv.FormatInt(Y.port, 10)
			}
		} @test(err, any, code=incomplete|cycle)

		Y: {
			if #Y.userinfo != _|_ {
				userinfo: #Y.userinfo
			}

			host: #Y.host

			if #Y.port != _|_ {
				port: strconv.Atoi(#Y.port)
			}
		} @test(err, any, code=incomplete|cycle)

		#Y: regexp.FindNamedSubmatch(#userHostPort, X) @test(err, any, code=incomplete|cycle)
	}
}
-- out/errors.txt --
[cycle] minimal.a: cyclic reference to field port:
    ./in.cue:12:8
[cycle] minimal.b: undefined field: port:
    ./in.cue:20:8
[cycle] small.p1.#Y: cyclic reference to field port:
    ./in.cue:33:50
[cycle] small.p1.#X: undefined field: port:
    ./in.cue:36:10
small.p1.#X: undefined field: port:
    ./in.cue:39:10
[cycle] medium.p1.#Y: undefined field: port:
    ./in.cue:59:50
[cycle] medium.p1.Y: undefined field: port:
    ./in.cue:66:10
[cycle] medium.p1.#X: cyclic reference to field port:
    ./in.cue:75:9
medium.p1.#X: cyclic reference to field port:
    ./in.cue:78:9
[cycle] large.p1.Y: undefined field: userinfo:
    ./in.cue:120:10
large.p1.Y: undefined field: port:
    ./in.cue:126:10
[cycle] large.p1.X: undefined field: port:
    ./in.cue:99:33
[cycle] large.p1.#X: circular dependency in evaluation of conditionals: Y.userinfo changed after evaluation:
    ./in.cue:102:7
large.p1.#X: circular dependency in evaluation of conditionals: Y.userinfo changed after evaluation:
    ./in.cue:105:7
large.p1.#X: cyclic reference to field port:
    ./in.cue:111:9
large.p1.#X: cyclic reference to field port:
    ./in.cue:114:9
[cycle] large.p1.X: undefined field: port:
    ./in.cue:99:33
-- out/todo.txt --
minimal.a/b: pushdown reports cycle error (matching v2) instead of incomplete (v3).
small.p1: pushdown resolves #Y to {port: ""} (matching v2); #X still has cycle error.
medium.p1: pushdown resolves #Y and #X; Y gets cycle error.
large.p1: pushdown resolves #Y, #X, X; Y gets cycle error. Ordering sensitive.
-- out/eval/stats --
Leaks:  13
Freed:  26
Reused: 11
Allocs: 28
Retain: 0

Unifications: 39
Conjuncts:    31
Disjuncts:    0

NumCloseIDs: 21
