vtest "VCL compiler coverage test: vcc_parse.c & vcc_expr.c"

vinyl v1 -cliok "param.set vcc_feature +allow_inline_c" -vcl {
	backend b { .host = "${localhost}"; }
	C{
	#include <stdio.h>
	}C
}

vinyl v1 -errvcl {Found: '0' at} { 0; }

# The next test issues a quite confusing error message:
#   Expected an action, 'if', '{' or '}'\n
#   ('Default' Line 42 Pos 1)\n
#   sub vcl_recv {\n
#   ###-----------\n
#   \n
# It's actually complaining about the first token in
# the default.vcl which is appended after the proffered
# VCLs tokenstream.
# XXX: A better error message would be desirable

vinyl v1 -errvcl {Symbol cannot be used here} " sub vcl_recv { { } { "

vinyl v1 -errvcl {Comparison of different types: INT '!=' STRING} {
	sub vcl_recv {
		if (!req.restarts != req.url) {
			set req.http.foo = "foo";
		}

	}
}

vinyl v1 -errvcl {Symbol 'vcl_recv' can only be used as a SUB expression} {
	backend proforma none;
	sub vcl_recv {
		set req.http.foo = vcl_recv;
	}
}

vinyl v1 -errvcl {Symbol 'vcl_recv' can only be used as a SUB expression} {
	backend proforma none;
	sub vcl_recv {
		return (synth(200));
	}
	sub vcl_synth {
		set req.http.foo = vcl_recv;
	}
}

vinyl v1 -errvcl {Symbol 'asub' can only be used as a SUB expression} {
	backend proforma none;
	sub asub {}
	sub vcl_recv {
		set req.http.foo = asub;
	}
}

vinyl v1 -errvcl {Symbol 'acl' type (reserved) cannot be used in expression.} {
	import std;
	sub vcl_recv { call acl; }
}

vinyl v1 -errvcl {Symbols named 'vcl_*' are reserved.} {
	import debug;

	sub vcl_recv {
		debug.test_probe(vcl_sub1, vcl_sub2);
	}
}

vinyl v1 -errvcl {Operator * not possible on type STRING.} {
	sub vcl_recv {
		set req.http.foo = "bla" * "foo";
	}
}

vinyl v1 -errvcl {DURATION + INT not possible.} {
	sub vcl_backend_response {
		set req.max_age = req.max_age + beresp.status;
	}
}

vinyl v1 -errvcl {BOOL + BOOL not possible.} {
	sub vcl_backend_response {
		if (beresp.do_gzip + beresp.do_gunzip) {
		}
	}
}

vinyl v1 -errvcl {Operator % only possible on INT} {
	sub vcl_recv {
		if (req.max_age % 1000) {
		}
	}
}

vinyl v1 -vcl {
	sub vcl_recv {
		# 3555 (define after use)
		set req.backend_hint = b;
	}
	backend b { .host = "${localhost}"; }
	sub vcl_recv {
		set req.http.foo = "foo" + "bar";
		set req.http.foo = "foo" + 1;
		set req.http.foo = "foo" + 1.5;
		set req.http.foo = "foo" + now;

		set req.http.foo = now + 1s;
		set req.http.foo = now - 1s;

		set req.http.foo = now - now;
		set req.http.foo = 1 + 1s;

		set req.http.foo = 1 + 1;
		set req.http.foo = 1 - 1;
		set req.http.foo = 1 + -1;
		set req.http.foo = 1 - -1;
		set req.http.foo = 3 * 2;
		set req.http.foo = 3 / 2;
		set req.http.foo = 3 * -2;
		set req.http.foo = 3 / -2;

		set req.http.foo = 3.6 + 1.4;
		set req.http.foo = 3.6 - 1.4;
		set req.http.foo = 3.6 + -1.4;
		set req.http.foo = 3.6 - -1.4;
		set req.http.foo = 3.6 * 1.4;
		set req.http.foo = 3.6 / 1.4;
		set req.http.foo = 3.6 * -1.4;
		set req.http.foo = 3.6 / -1.4;

		set req.http.foo = 1.0 + 1;
		set req.http.foo = 1.0 - 1;
		set req.http.foo = 1.0 + -1;
		set req.http.foo = 1.0 - -1;
		set req.http.foo = 3.0 * 2;
		set req.http.foo = 3.0 / 2;
		set req.http.foo = 3.0 * -2;
		set req.http.foo = 3.0 / -2;

		set req.http.foo = req.http.foo + "bar" ~ "bar";
		set req.http.foo = req.http.foo + "bar" !~ "bar";

		set req.http.foo = "foo" + req.max_age;

		set req.http.foo = client.ip + ", " + server.ip;

		set req.max_age = -1s;
		set req.max_age = 1s;
		set req.max_age *= 1.5;
		set req.max_age = 1.5 s * 2.5;
		set req.max_age = 1.5 s / 2.5;
		set req.max_age = 1.5h + 1.5s;
		set req.max_age = 1.5h - 1.5s;

		if (req.max_age) { }
		if (!req.max_age) { }
		if (req.max_age > 1d) { }
		if (req.max_age < 1d) { }

		if (1) { }
		if (2 == 3) { }
		if (2 < 3) { }
		if (2 > 3) { }

		if (req.backend_hint == b) { }
		if (req.backend_hint != b) { }

		if (req.url) { }
		if (!req.url) { }
		if (req.url == "foo") { }
		if (req.url != "foo") { }
		if (req.url ~ "foo") { }
		if (req.url !~ "foo") { }

		if (1) { }
		elsif (2) { }
		elseif (3) { }
		else if (4) { }
		else { }

		# regression test for #2729
		if (req.grace < 0s || req.grace < 1s && req.grace < 2s) {
			return (pass);
		}
	}
}

# XXX: not the most clear error message
vinyl v1 -errvcl {STRING - STRING not possible.} {
	sub vcl_recv {
		set req.http.foo = "foo" - "bar";
	}
}

vinyl v1 -errvcl {TIME + STRING not possible.} {
	sub vcl_recv {
		set req.max_age = now + "foo";
	}
}

vinyl v1 -errvcl {TIME + TIME not possible.} {
	sub vcl_recv {
		set req.max_age = now + now;
	}
}

vinyl v1 -errvcl {INT + STRING not possible.} {
	sub vcl_backend_response {
		set beresp.status = 1 + "foo";
	}
}

vinyl v1 -errvcl {INT + TIME not possible.} {
	sub vcl_backend_response {
		set beresp.status = 1 + now;
	}
}

vinyl v1 -errvcl {DURATION + INT not possible.} {
	sub vcl_recv {
		set req.max_age = 1s + 1;
	}
}

vinyl v1 -errvcl {DURATION + TIME not possible.} {
	sub vcl_recv {
		set req.max_age = 1s + now;
	}
}

vinyl v1 -errvcl {DURATION + STRING not possible.} {
	sub vcl_recv {
		set req.max_age = 1s + "foo";
	}
}

vinyl v1 -errvcl {IP + IP not possible.} {
	sub vcl_recv {
		set req.max_age = client.ip + server.ip;
	}
}

vinyl v1 -errvcl {Name of subroutine, 'foo.bar', contains illegal character '.'} {
	sub foo.bar {
	}
	sub vcl_recv {
		call foo.bar;
	}
}

# 3555 (define after use)
vinyl v1 -vcl {
	import debug;
	backend be none;
	sub caller {
		debug.call(foo);
	}
	sub foo { }
	sub vcl_recv {
		call caller;
		call foo;
	}
}

# 3555 (don't launder partial symbols)
vinyl v1 -errvcl {Symbol not found: 'foo.bar'} {
	sub vcl_recv {
		set req.backend_hint = foo.bar;
	}
}

vinyl v1 -errvcl {The names 'vcl_*' are reserved for subroutines.} {
	sub vcl_bar {
	}
	sub vcl_recv {
		call vcl_bar;
	}
}

vinyl v1 -errvcl {Function returns VOID} {
	import vtc;
	sub vcl_recv {
		set req.http.foo = vtc.sleep(1m);
	}
}

vinyl v1 -errvcl {Not available in subroutine 'vcl_recv'.} {
	import blob;
	backend b { .host = "${localhost}"; }
	sub vcl_recv {
		blob.encode(HEX, LOWER, req.hash);
	}
}

vinyl v1 -errvcl {Not available in subroutine 'vcl_hash'.} {
	import blob;
	backend b { .host = "${localhost}"; }
	sub vcl_hash {
		blob.encode(HEX, LOWER, req.hash);
	}
}

vinyl v1 -errvcl {Not available in subroutine 'vcl_recv'.} {
	backend b { .host = "${localhost}"; }
	sub vcl_recv {
		set req.http.foo = 100 + beresp.status;
	}
}

vinyl v1 -cliok "param.set vcc_feature -err_unref"

vinyl v1 -errvcl {Impossible Subroutine} {
	backend b { .host = "127.0.0.1"; }
	sub foo {
		set req.http.foo = 100 + beresp.status;
	}
}

vinyl v1 -cliok "param.set vcc_feature +err_unref"

vinyl v1 -errvcl {
('<vcl.inline>' Line 4 Pos 44) -- (Pos 49)
        sub foo { set req.http.foo = 100 + beresp.status; }
-------------------------------------------######----------

Not available from subroutine 'vcl_recv'.

...in subroutine "foo"
('<vcl.inline>' Line 4 Pos 13)
        sub foo { set req.http.foo = 100 + beresp.status; }
------------###--------------------------------------------


...called from "vcl_recv"
('<vcl.inline>' Line 5 Pos 29)
        sub vcl_recv { call foo; }
----------------------------###---
} {
	backend b { .host = "${localhost}"; }
	sub foo { set req.http.foo = 100 + beresp.status; }
	sub vcl_recv { call foo; }
}

vinyl v1 -errvcl {
('<vcl.inline>' Line 4 Pos 44) -- (Pos 49)
        sub foo { set req.http.foo = 100 + beresp.status; }
-------------------------------------------######----------

Not available from subroutine 'vcl_recv'.

...in subroutine "foo"
('<vcl.inline>' Line 4 Pos 13)
        sub foo { set req.http.foo = 100 + beresp.status; }
------------###--------------------------------------------


...called from "bar"
('<vcl.inline>' Line 5 Pos 24)
        sub bar { call foo; }
-----------------------###---


...called from "vcl_recv"
('<vcl.inline>' Line 6 Pos 29)
        sub vcl_recv { call bar; }
----------------------------###---
} {
	backend b { .host = "${localhost}"; }
	sub foo { set req.http.foo = 100 + beresp.status; }
	sub bar { call foo; }
	sub vcl_recv { call bar; }
}

vinyl v1 -errvcl {Name of ACL, 'foo.bar', contains illegal character '.'} {
	acl foo.bar {
	}
	sub vcl_recv {
		if (client.ip ~ foo.bar) {
		}
	}
}

vinyl v1 -errvcl {Expected 'from path ...'} {
	import std to;
}

vinyl v1 -errvcl {INT * BLOB not possible.} {
	import blob;
	sub vcl_deliver {
		set resp.status = 100 * blob.decode(HEX, encoded="a");
	}
}

vinyl v1 -errvcl {INT * BLOB not possible.} {
	import blob;
	sub vcl_deliver {
		set resp.status = 100 * :4thASR0O18ZxnoKtc4zd8KuO25rPvwvMQyAvRfilz6o=:);
	}
}

# XXX: should spot nonexistent storage
vinyl v1 -errvcl {Symbol not found: 'storage.foo'} {
	sub vcl_backend_response {
		set beresp.storage = storage.foo;
	}
}

vinyl v1 -errvcl {Comparison of different types: BACKEND '==' STRING} {
	sub vcl_backend_response {
		set beresp.http.bereq_backend = bereq.backend;
		if (bereq.backend == beresp.http.bereq_backend) {
			set beresp.http.bereq_backend_cmp = "ok";
		}
	}
}

vinyl v1 -errvcl {'!' must be followed by BOOL, found REAL.} {
	import std;
	sub vcl_recv { if (!std.random(0,1)) { } }
}

vinyl v1 -errvcl {'&&' must be followed by BOOL, found REAL.} {
	import std;
	sub vcl_recv { if (0 && std.random(0,1)) { } }
}

vinyl v1 -errvcl {'||' must be followed by BOOL, found REAL.} {
	import std;
	sub vcl_recv { if (0 || std.random(0,1)) { } }
}

vinyl v1 -errvcl {'&&' must be preceded by BOOL, found REAL.} {
	import std;
	sub vcl_recv { if (std.random(0,1) && 0) { } }
}

vinyl v1 -errvcl {'||' must be preceded by BOOL, found REAL.} {
	import std;
	sub vcl_recv { if (std.random(0,1) || 0) { } }
}

vinyl v1 -errvcl {Symbol 'acl' type (reserved) cannot be used in expression.} {
	import std;
	sub vcl_recv { if (client.ip ~ acl) {} }
}

vinyl v1 -errvcl {Symbol 'default' is a reserved word.} {
	import std;
	sub vcl_recv { set req.http.foo = default; }
}

vinyl v1 -errvcl {Cannot convert HTTP to STRING} {
	sub vcl_synth { set resp.http.foo = resp; }
}

server s1 {
	rxreq
	txresp -hdr "bar: X"
} -start

vinyl v1 -vcl+backend {
	// Ticket 2727
	sub vcl_recv {
		if ((2 - 1) > 0) {
			# nothing
		}
	}
}

vinyl v1 -vcl+backend {
	import std;
	import debug;
	sub vcl_deliver {
		// Ticket 2745
		debug.sethdr(resp.http.rst, req.restarts);
		set resp.http.foo =
		    (resp.http.foo + resp.http.bar) == ("X" + resp.http.foo);
		// Ticket 2809
		set resp.http.bar = true == false;

		set resp.http.a = req.http.inexistent;
		// correct? #3844
		set resp.http.a-eq-inexistent = resp.http.a == req.http.inexistent;
		// discussion in #3844 - and should !!hdr work?
		set resp.http.is-a = !(!resp.http.a);
		set resp.http.is-inexistent = !(!req.http.inexistent);
	}
	sub vcl_deliver {
		set resp.http.p = (0 + 999999999999999);
		set resp.http.n = (0 - 999999999999999);
		if (resp.status == -(-200)) {
			set resp.http.o = -std.integer("-200");
		}
	}
} -start

client c1 {
	txreq
	rxresp
	expect resp.http.rst == "0"
	expect resp.http.foo == "true"
	expect resp.http.bar == "false"
	expect resp.http.p == 999999999999999
	expect resp.http.n == -999999999999999
	expect resp.http.o == 200

	expect resp.http.a == ""
	# correct? #3844
	expect resp.http.a-eq-inexistent == "false"
	# discussion in #3844
	expect resp.http.is-a == "true"
	expect resp.http.is-inexistent == "false"
} -run

vinyl v1 -vcl {
	import std;
	backend be none;
	sub vcl_recv {
		return (synth(200));
	}
	sub vcl_synth {
		if (req.url ~ "double-minus") {
			set resp.status = -(-204);
		}
		if (req.url ~ "minus-std") {
			set resp.status = -std.integer("-204");
		}
		return (deliver);
	}
}

client c1 {
	txreq -url "/double-minus"
	rxresp
	expect resp.status == 204

	txreq -url "/minus-std"
	rxresp
	expect resp.status == 204
} -run
