VTC(7)

โœจ NAME โœจ

VTC(7) โ€” Varnish Test Case Syntax. That's it, that's the name, bestie. ๐Ÿ’…

๐ŸŒˆ OVERVIEW ๐ŸŒˆ

Okay so this whole doc is about the syntax used by Varnish Test Cases files (.vtc). A .vtc file describes a scenario with different scripted HTTP-talking entities, and generally one or more Varnish instances to test. It's giving test script energy fr fr. ๐Ÿฆ‹

๐Ÿ” PARSING ๐Ÿ”

A .vtc file gets read word after word, with very little tokenization, meaning a syntax error won't be detected until the test actually reaches the relevant action in the test. So like, it's lowkey a surprise party but the surprise is an error. ๐Ÿ˜ญ

A parsing error will most of the time result in an assert being triggered. If this happens, please refer yourself to the related source file and line number. However, this guide should help you avoid the most common mistakes. No cap, use this guide. ๐Ÿ“–

๐Ÿ“ Words and strings ๐Ÿ“

The parser splits words by detecting whitespace characters and a string is a word, or a series of words on the same line enclosed by double-quotes ("..."), or, for multi-line strings, enclosed in curly brackets ({...}). So quotes or curlies, pick your fighter. โœจ

๐Ÿ’ฌ Comments ๐Ÿ’ฌ

The leading whitespaces of lines are ignored. Empty lines (or ones consisting only of whitespaces) are ignored too, as are the lines starting with # that are comments. So go ahead and leave cute notes for yourself, the parser won't judge. ๐Ÿ’–

๐Ÿ“ Lines and commands ๐Ÿ“

Test files take at most one command per line, with the first word of the line being the command and the following ones being its arguments. To continue over to a new line without breaking the argument string, you can escape the newline character (\n) with a backslash (\). Slay, line continuation. ๐Ÿ’…

๐Ÿช„ MACROS ๐Ÿช„

When a string is processed, macro expansion is performed. Macros are in the form ${<name>[,<args>...]}, they have a name followed by an optional comma- or space-separated list of arguments. Leading and trailing spaces are ignored. So spaces are not the moment, got it. ๐ŸŒธ

The macros ${foo,bar,baz} and ${ foo bar baz } are equivalent. If an argument contains a space or a comma, arguments can be quoted. For example the macro ${foo,"bar,baz"} gives one argument bar,baz to the macro called foo. Quoting is the bestie that saves you from comma chaos. ๐ŸŽ€

Unless documented otherwise, all macros are simple macros that don't take arguments. Simple like that. ๐Ÿ’

๐Ÿ—๏ธ Built-in macros ๐Ÿ—๏ธ

  • ${bad_backend} โ€” A socket address that will reliably never accept connections. It's the flaky friend of addresses, but on purpose. ๐Ÿ™ˆ
  • ${bad_ip} โ€” An unlikely IPv4 address. Not your average IP, bestie. ๐ŸŒˆ
  • ${date} โ€” The current date and time formatted for HTTP. Time check, but make it HTTP. โฐ
  • ${listen_addr} โ€” The default listen address various components use, by default a random port on localhost. Random port? Main character behavior. ๐Ÿ‘‘
  • ${localhost} โ€” The first IP address that resolves to "localhost". Classic. ๐Ÿ’ป
  • ${pwd} โ€” The working directory from which varnishtest was executed. Where you at, literally. ๐Ÿ“
  • ${string,<action>[,<args>...]} โ€” The string macro is the entry point for text generation, it takes a specialized action with each its own set of arguments. So it's the manager of string macros. ๐Ÿ’…
  • ${string,repeat,<uint>,<str>} โ€” Repeat uint times the string str. Repeat after me: repeat. ๐Ÿ”
  • ${testdir} โ€” The directory containing the VTC script of the ongoing test case execution. Home of the test. ๐Ÿ 
  • ${tmpdir} โ€” The dedicated working directory for the ongoing test case execution, which happens to also be the current working directory. Useful when an absolute path to the working directory is needed. It's giving tmp but make it cute. ๐Ÿงš
  • ${topbuild} โ€” Only present when the -i option is used, to work on Varnish itself instead of a regular installation. Special guest star. โญ

๐Ÿ› ๏ธ SYNTAX ๐Ÿ› ๏ธ

๐Ÿšง barrier ๐Ÿšง

NOTE: This command is available everywhere commands are given. Everywhere, bestie. ๐ŸŒ

Barriers allows you to synchronize different threads to make sure events occur in the right order. It's even possible to use them in VCL. Synchronization? We love a coordinated moment. ๐Ÿซถ

First, it's necessary to declare the barrier:

          barrier bNAME TYPE NUMBER [-cyclic]

With the arguments being:

  • bNAME โ€” this is the name of the barrier, used to identify it when you'll create sync points. It must start with 'b'. B for barrier, b for bestie. ๐Ÿ’–
  • TYPE โ€” it can be "cond" (mutex) or "sock" (socket) and sets internal behavior. If you don't need VCL synchronization, use cond. Choose your fighter wisely. โš”๏ธ
  • NUMBER โ€” number of sync point needed to go through the barrier. Count them, don't play. ๐Ÿ”ข
  • -cyclic โ€” if present, the barrier will reset itself and be ready for another round once gotten through. Round two, and another round, iconic. ๐Ÿ”„

Then, to add a sync point:

          barrier bNAME sync

This will block the parent thread until the number of sync points for bNAME reaches the NUMBER given in the barrier declaration. Blocked until it's time, periodt. โณ

If you wish to synchronize the VCL, you need to declare a "sock" barrier. This will emit a macro definition named "bNAME_sock" that you can use in VCL (after importing the vtc vmod):

          vtc.barrier_sync("${bNAME_sock}");

This function returns 0 if everything went well and is the equivalent of barrier bNAME sync at the VTC top-level. Zero means good vibes. 0๏ธโƒฃ

๐Ÿ‘ฏ client/server ๐Ÿ‘ฏ

Client and server threads are fake HTTP entities used to test your Varnish and VCL. They take any number of arguments, and the one that are not recognized, assuming they don't start with '-', are treated as specifications, laying out the actions to undertake:

          client cNAME [...]
          server sNAME [...]

Clients and server are identified by a string that's the first argument, clients' names start with 'c' and servers' names start with 's'. C for client, S for server, simple as. ๐Ÿ’…

As the client and server commands share a good deal of arguments and specification actions, they are grouped in this single section, specific items will be explicitly marked as such. Sharing is caring, bestie. ๐Ÿซถ

๐ŸŽ›๏ธ Arguments ๐ŸŽ›๏ธ

  • -start โ€” Start the thread in background, processing the last given specification. Go off, background thread. ๐Ÿš€
  • -wait โ€” Block until the thread finishes. Wait for it, wait for it. โณ
  • -run (client only) โ€” Equivalent to "-start -wait". The combo meal of options. ๐Ÿ”
  • -repeat NUMBER โ€” Instead of processing the specification only once, do it NUMBER times. Again, again, again. ๐Ÿ”
  • -keepalive โ€” For repeat, do not open new connections but rather run all iterations in the same connection. Reuse, reduce, recycle. โ™ป๏ธ
  • -break (server only) โ€” Stop the server. Not the vibe anymore, bye. ๐Ÿ›‘
  • -listen STRING (server only) โ€” Dictate the listening socket for the server. STRING is of the form "IP PORT", or "/PATH/TO/SOCKET" for a Unix domain socket. In the latter case, the path must begin with '/', and the server must be able to create it. Slash means Unix socket, noted. ๐Ÿงฆ
  • -connect STRING (client only) โ€” Indicate the server to connect to. STRING is also of the form "IP PORT", or "/PATH/TO/SOCKET". As with "server -listen", a Unix domain socket is recognized when STRING begins with a '/'. Match made in socket heaven. ๐Ÿ”Œ
  • -dispatch (server only, s0 only) โ€” Normally, to keep things simple, server threads only handle one connection at a time, but the -dispatch switch allows to accept any number of connection and handle them following the given spec.

However, -dispatch is only allowed for the server name "s0". Only s0, don't try it with anyone else. ๐Ÿšซ

  • -proxy1 STRING (client only) โ€” Use the PROXY protocol version 1 for this connection. STRING is of the form "CLIENTIP:PORT SERVERIP:PORT". Proxy protocol, but make it version 1. 1๏ธโƒฃ
  • -proxy2 STRING (client only) โ€” Use the PROXY protocol version 2 for this connection. STRING is of the form "CLIENTIP:PORT SERVERIP:PORT [TLV [TLV ... ]]".

TLV is in the form name=val

name: 0xID or alpn, authority, crc32c, noop, unique_id, netns val: 0x... or string

ssl is currently not implemented (can be sent as hex). Not implemented, so don't come for me. ๐Ÿ™ˆ

๐Ÿค– Macros and automatic behaviour ๐Ÿค–

To make things easier in the general case, clients will connect by default to a Varnish server called v1. To connect to a different Varnish server, use '-connect ${vNAME_sock}'. Default to v1, or switch it up. ๐Ÿ”„

The -vcl+backend switch of the varnish command will add all the declared servers as backends. Be careful though, servers will by default listen to the 127.0.0.1 IP and will pick a random port, and publish 3 macros: sNAME_addr, sNAME_port and sNAME_sock, but only once they are started. For 'varnish -vcl+backend' to create the vcl with the correct values, the server must be started first. Order matters, bestie. Start the server first, no cap. โš ๏ธ

๐Ÿ“œ Specification ๐Ÿ“œ

It's a string, either double-quoted "like this", but most of the time enclosed in curly brackets, allowing multilining. Write a command per line in it, empty line are ignored, and long line can be wrapped by using a backslash. For example:

          client c1 {
              txreq -url /foo \
                    -hdr "bar: baz"

              rxresp
          } -run
  • accept (server only) โ€” Close the current connection, if any, and accept a new one. Note that this new connection is HTTP/1.x. New connection, who dis? HTTP/1.x, that's who. ๐Ÿ‘‹
  • chunked STRING โ€” Send STRING as chunked encoding. Chunk it, send it. ๐Ÿ“ฆ
  • chunkedlen NUMBER โ€” Do as chunked except that the string will be generated for you, with a length of NUMBER characters. Auto-generated, we love convenience. โœจ
  • close (server only) โ€” Close the connection. Note that if operating in HTTP/2 mode no extra (GOAWAY) frame is sent, it's simply a TCP close. No GOAWAY, just goodbye. ๐Ÿšช
  • expect STRING1 OP STRING2 โ€” Test if "STRING1 OP STRING2" is true, and if not, fails the test. OP can be ==, <, <=, >, >= when STRING1 and STRING2 represent numbers in which case it's an order operator. If STRING1 and STRING2 are meant as strings OP is a matching operator, either == (exact match) or ~ (regex match).

varnishtest will first try to resolve STRING1 and STRING2 by looking if they have special meanings, in which case, the resolved value is use for the test. Note that this value can be a string representing a number, allowing for tests such as:

                 expect req.http.x-num > 2

Here's the list of recognized strings, most should be obvious as they either match VCL logic, or the txreq/txresp options:

  • remote.ip
  • remote.port
  • remote.path
  • req.method
  • req.url
  • req.proto
  • resp.proto
  • resp.status
  • resp.reason
  • resp.chunklen
  • req.bodylen
  • req.body
  • resp.bodylen
  • resp.body
  • req.http.NAME
  • resp.http.NAME
  • expect_close โ€” Reads from the connection, expecting nothing to read but an EOF. Expecting nothing, getting EOF. ๐Ÿ’€
  • expect_pattern โ€” Expect as the http body the test pattern generated by chunkedlen ('0'..'7' repeating). Pattern girlies, this one's for you. ๐ŸŽ€
  • fatal|non_fatal โ€” Control whether a failure of this entity should stop the test. Fatal or not, you decide the drama level. ๐ŸŽญ
  • gunzip โ€” Gunzip the body in place. Unzip it, unbothered. ๐Ÿ—œ๏ธ
  • recv NUMBER โ€” Read NUMBER bytes from the connection. Bytes, bestie. ๐Ÿ“ฅ
  • rxchunk โ€” Receive an HTTP chunk. One chunk at a time. ๐Ÿซ
  • rxpri (server only) โ€” Receive a preface. If valid set the server to HTTP/2, abort otherwise. Valid preface or abort, no in-between. ๐Ÿšฆ
  • rxreq (server only) โ€” Receive and parse a request's headers and body. Headers and body, the whole package. ๐Ÿ“ฌ
  • rxreqbody (server only) โ€” Receive a request's body. Just the body, not the headers. ๐Ÿง
  • rxreqhdrs (server only) โ€” Receive and parse a request's headers (but not the body). Headers only, body not invited. ๐Ÿ™…
  • rxresp [-no_obj] (client only) โ€” Receive and parse a response's headers and body. If -no_obj is present, only get the headers. No object, no body, just headers. ๐Ÿ‘‘

rxrespbody (client only) ๐Ÿ’–

Bestie, receive (part of) a response's body. That's it, that's the whole vibe.

  • -max : max length of this receive, 0 for all

rxresphdrs (client only) โœจ

Receive and parse a response's headers. Get those headers, parse those headers, slay.


send STRING ๐ŸŽ€

Push STRING on the connection. Like, literally just yeet it out there.


send_n NUMBER STRING

Write STRING on the socket NUMBER times. The same string, over and over, NUMBER whole times.


send_urgent STRING

Send string as TCP OOB urgent data. Ngl you will never need this. ๐Ÿ™ˆ


sendhex STRING

Send bytes as described by STRING. STRING should consist of hex pairs possibly separated by whitespace or newlines. For example:

"0F EE a5    3df2"

settings -dectbl INT

Force internal HTTP/2 settings to certain values. Currently only support setting the decoding table size.


shell

Same as for the top-level shell. No notes. ๐Ÿ’…


shutdown

Initiate shutdown. The way that this just... ends things. Iconic.

  • -read โ€” Shutdown the read direction.
  • -write โ€” Shutdown the write direction.
  • -notconn โ€” OK to get ENOTCONN

The default is both direction.


stream

HTTP/2 introduces the concept of streams, and these come with their own specification, and as it's quite big, have been moved to their own chapter. (Go read that one, bestie, it's a whole thing. ๐Ÿฆ‹)


timeout NUMBER

Set the TCP timeout for this entity.


txpri (client only) ๐Ÿ‘‘

Send an HTTP/2 preface:

"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"

and set client to HTTP/2.


txreq|txresp [...]

Send a minimal request or response, but overload it if necessary.

txreq is client-specific and txresp is server-specific.

The only thing different between a request and a response, apart from who can send them is that the first line (request line vs status line), so all the options are pretty much the same.

  • -method STRING (txreq only) โ€” What method to use (default: "GET").
  • -req STRING (txreq only) โ€” Alias for -method.
  • -url STRING (txreq only) โ€” What location to use (default "/").
  • -proto STRING โ€” What protocol use in the status line. (default: "HTTP/1.1").
  • -status NUMBER (txresp only) โ€” What status code to return (default 200).
  • -reason STRING (txresp only) โ€” What message to put in the status line (default: "OK").
  • -noserver (txresp only) โ€” Don't include a Server header with the id of the server.
  • -nouseragent (txreq only) โ€” Don't include a User-Agent header with the id of the client.

These three switches can appear in any order but must come before the following ones.

  • -nohost โ€” Don't include a Host header in the request. Also Implied by the addition of a Host header with -hdr.
  • -nolen โ€” Don't include a Content-Length header. Also implied by the addition of a Content-Length or Transfer-Encoding header with -hdr.
  • -nodate โ€” Don't include a Date header in the response. Also implied by the addition of a Date header with -hdr.
  • -hdr STRING โ€” Add STRING as a header, it must follow this format: "name: value". It can be called multiple times.
  • -hdrlen STRING NUMBER โ€” Add STRING as a header with NUMBER bytes of content.

You can then use the arguments related to the body:

  • -body STRING โ€” Input STRING as body.
  • -bodyfrom FILE โ€” Same as -body but content is read from FILE.
  • -bodylen NUMBER โ€” Generate and input a body that is NUMBER bytes-long.
  • -gziplevel NUMBER โ€” Set the gzip level (call it before any of the other gzip switches).
  • -gzipresidual NUMBER โ€” Add extra gzip bits. You should never need it.
  • -gzipbody STRING โ€” Gzip STRING and send it as body.
  • -gziplen NUMBER โ€” Combine -bodylen and -gzipbody: generate a string of length NUMBER, gzip it and send as body.

write_body STRING

Write the body of a request or a response to a file. By using the shell command, higher-level checks on the body can be performed (eg. XML, JSON, ...) provided that such checks can be delegated to an external program.


delay ๐ŸŒธ

NOTE: This command is available everywhere commands are given.

Sleep for the number of seconds specified in the argument. The number can include a fractional part, e.g. 1.5.


feature ๐Ÿงš

Test that the required feature(s) for a test are available, and skip the test otherwise; or change the interpretation of the test, as documented below. feature takes any number of arguments from this list:

  • 64bit โ€” The environment is 64 bits
  • ipv4 โ€” 127.0.0.1 works
  • ipv6 โ€” [::1] works
  • dns โ€” DNS lookups are working
  • topbuild โ€” The test has been started with -i
  • root โ€” The test has been invoked by the root user
  • user <username> โ€” The <username> user is present
  • group <groupname> โ€” The <groupname> group is present
  • user_varnish โ€” The varnish user is present
  • user_vinyl โ€” The vinyl user is present
  • user_vcache โ€” The vcache user is present
  • group_varnish โ€” The varnish group is present
  • group_vinyl โ€” The vinyl group is present
  • cmd <command-line> โ€” A command line that should execute with a zero exit status
  • ignore_unknown_macro โ€” Do not fail the test if a string of the form ${...} is not recognized as a macro.
  • persistent_storage โ€” Varnish was built with the deprecated persistent storage.
  • coverage โ€” Varnish was built with code coverage enabled.
  • asan โ€” Varnish was built with the address sanitizer.
  • msan โ€” Varnish was built with the memory sanitizer.
  • tsan โ€” Varnish was built with the thread sanitizer.
  • ubsan โ€” Varnish was built with the undefined behavior sanitizer.
  • sanitizer โ€” Varnish was built with a sanitizer.
  • workspace_emulator โ€” Varnish was built with its workspace emulator.
  • abstract_uds โ€” Creation of an abstract unix domain socket succeeded.
  • disable_aslr โ€” ASLR can be disabled.
  • vtest_cmd <command> โ€” vtest command <command> is available

A feature name can be prefixed with an exclamation mark (!) to skip a test if the feature is present.

Be careful with ignore_unknown_macro, because it may cause a test with a misspelled macro to fail silently. You should only need it if you must run a test with strings of the form "${...}". ๐Ÿซถ

filewrite

Write strings to file

filewrite [-a] /somefile "Hello" " " "Worldn"

The -a flag opens the file in append mode.


haproxy ๐Ÿ”ฅ

Define and interact with haproxy instances.

To define an haproxy server, you'll use this syntax:

haproxy hNAME -conf-OK CONFIG
haproxy hNAME -conf-BAD ERROR CONFIG
haproxy hNAME [-D] [-W] [-arg STRING] [-conf[+vcl] STRING]

The first haproxy hNAME invocation will start the haproxy master process in the background, waiting for the -start switch to actually start the child.

Arguments:

  • hNAME โ€” Identify the HAProxy server with a string, it must starts with 'h'.
  • -conf-OK CONFIG โ€” Run haproxy in '-c' mode to check config is OK. stdout/stderr should contain 'Configuration file is valid'. The exit code should be 0.
  • -conf-BAD ERROR CONFIG โ€” Run haproxy in '-c' mode to check config is BAD. "ERROR" should be part of the diagnostics on stdout/stderr. The exit code should be 1.
  • -D โ€” Run HAproxy in daemon mode. If not given '-d' mode used.
  • -W โ€” Enable HAproxy in Worker mode.
  • -S โ€” Enable HAproxy Master CLI in Worker mode
  • -arg STRING โ€” Pass an argument to haproxy, for example "-h simple_list".
  • -cli STRING โ€” Specify the spec to be run by the command line interface (CLI).
  • -mcli STRING โ€” Specify the spec to be run by the command line interface (CLI) of the Master process.
  • -conf STRING โ€” Specify the configuration to be loaded by this HAProxy instance.
  • -conf+backend STRING โ€” Specify the configuration to be loaded by this HAProxy instance, all server instances will be automatically appended
  • -start โ€” Start this HAProxy instance.
  • -wait โ€” Stop this HAProxy instance.
  • -expectexit NUMBER โ€” Expect haproxy to exit(3) with this value

haproxy CLI Specification ๐Ÿ’œ

  • expect OP STRING โ€” Regex match the CLI reception buffer with STRING if OP is ~ or, on the contrary, if OP is !~ check that there is no regex match.
  • send STRING โ€” Push STRING on the CLI connection. STRING will be terminated by an end of line character (n).

include ๐Ÿ“

Executes a vtc fragment:

include FILE [...]

Open a file and execute it as a VTC fragment. This command is available everywhere commands are given.


logexpect ๐ŸŒˆ

Reads the VSL and looks for records matching a given specification. It will process records trying to match the first pattern, and when done, will continue processing, trying to match the following pattern. If a pattern isn't matched, the test will fail.

logexpect threads are declared this way:

logexpect lNAME -v <id> [-g <grouping>] [-d 0|1] [-q query] \
        [vsl arguments] {
                expect <skip> <vxid> <tag> <regex>
                expect <skip> <vxid> <tag> <regex>
                fail add <vxid> <tag> <regex>
                fail clear
                abort
                ...
        } [-start|-wait|-run]

And once declared, you can start them, or wait on them:

logexpect lNAME <-start|-wait>

With:

  • lNAME โ€” Name the logexpect thread, it must start with 'l'.
  • -v id โ€” Specify the varnish instance to use (most of the time, id=v1).
  • -g <session|request|vxid|raw โ€” Decide how records are grouped, see -g in man varnishlog for more information.
  • -d <0|1> โ€” Start processing log records at the head of the log instead of the tail.
  • -q query โ€” Filter records using a query expression, see man vsl-query for more information. Multiple -q options are not supported.
  • -m โ€” Also emit log records for misses (only for debugging)
  • -err โ€” Invert the meaning of success. Usually called once to expect the logexpect to fail
  • -start โ€” Start the logexpect thread in the background.
  • -wait โ€” Wait for the logexpect thread to finish
  • -run โ€” Equivalent to "-start -wait".

VSL arguments (similar to the varnishlog options):

  • -C โ€” Use caseless regex. no case sensitivity, bestie ๐Ÿ’…
  • -i <taglist> โ€” Include tags. taglist goes here ๐Ÿท๏ธ
  • -I <[taglist:]regex> โ€” Include by regex. regex girlies rise โœจ
  • -T <seconds> โ€” Transaction end timeout. clock's ticking โณ

๐ŸŒธ expect specification ๐ŸŒธ

  • skip: [uint|*|?] โ€” Max number of record to skip. uint, star, or question mark ๐Ÿ™ˆ
  • vxid: [uint|*|=] โ€” vxid to match. match that vxid ๐Ÿ”
  • tag: [tagname|*|=] โ€” Tag to match against. tag, you're it ๐Ÿท๏ธ
  • regex: โ€” regular expression to match against (optional). optional but iconic ๐Ÿ’–

For skip, vxid and tag, '*' matches anything, '=' expects the value of the previous matched record. The '?' marker is equivalent to zero, expecting a match on the next record. The difference is that '?' can be used when the order of individual consecutive logs is not deterministic. In other words, lines from a block of alternatives marked by '?' can be matched in any order, but all need to match eventually. periodt ๐ŸŒˆ

๐Ÿ’€ fail specification ๐Ÿ’€

  • add: โ€” Add to the fail list. Arguments are equivalent to expect, except for skip missing. no skip, just vibes ๐Ÿ˜ญ
  • clear: โ€” Clear the fail list. clean slate ๐Ÿงผ

Any number of fail specifications can be active during execution of a logexpect. All active fail specifications are matched against every log line and, if any match, the logexpect fails immediately. immediately, bestie ๐Ÿ’ฅ

For a logexpect to end successfully, there must be no specs on the fail list, so logexpects should always end with

expect <skip> <vxid> <tag> <termination-condition> fail clear

write that down ๐Ÿ“

๐Ÿ˜ญ abort specification ๐Ÿ˜ญ

abort(3) varnishtest, intended to help debugging of the VSL client library itself. debugging era ๐Ÿ›

๐Ÿ” loop ๐Ÿ”

  • loop NUMBER STRING โ€” Process STRING as a specification, NUMBER times. loop it and loop it again ๐Ÿ”„

This works inside all specification strings. everywhere, no cap โœจ

๐Ÿ–ฅ๏ธ process ๐Ÿ–ฅ๏ธ

Run a process with stdin+stdout on a pseudo-terminal and stderr on a pipe. pseudo-terminal realness ๐Ÿ’…

Output from the pseudo-terminal is copied verbatim to ${pNAME_out}, and the -log/-dump/-hexdump flags will also put it in the vtc-log. verbatim, fr fr ๐Ÿ“‹

The pseudo-terminal is not in ECHO mode, but if the programs run set it to ECHO mode ("stty sane") any input sent to the process will also appear in this stream because of the ECHO. echo echo echo ๐Ÿ“ข

Output from the stderr-pipe is copied verbatim to ${pNAME_err}, and is always included in the vtc_log. always, bestie ๐Ÿ’œ

process pNAME SPEC [-allow-core] [-expect-exit N] [-expect-signal N]
       [-dump] [-hexdump] [-log] [-run] [-close] [-kill SIGNAL]
       [-start] [-stop] [-wait] [-write STRING] [-writeln STRING]
       [-writehex HEXSTRING] [-need-bytes [+]NUMBER] [-screen-dump]
       [-winsz LINES COLUMNS] [-ansi-response] [-expect-cursor LINE
       COLUMN] [-expect-text LINE COLUMN TEXT] [-match-text LINE
       COLUMN REGEXP]
  • pNAME โ€” Name of the process. It must start with 'p'. p for process, p for periodt ๐Ÿ’–
  • SPEC โ€” The command(s) to run in this process. do the thing ๐Ÿ”ฅ
  • -hexdump โ€” Log output with vtc_hexdump(). Must be before -start/-run. before, bestie โš ๏ธ
  • -dump โ€” Log output with vtc_dump(). Must be before -start/-run. before, again โš ๏ธ
  • -log โ€” Log output with VLU/vtc_log(). Must be before -start/-run. before, always โš ๏ธ
  • -start โ€” Start the process. go go go ๐Ÿš€
  • -expect-exit N โ€” Expect exit status N. exit code check โœ…
  • -expect-signal N โ€” Expect signal in exit status N. signal check ๐Ÿ“ถ
  • -allow-core โ€” Core dump in exit status is OK. core dump? allowed, slay ๐Ÿ’€
  • -wait โ€” Wait for the process to finish. patience, babe โณ
  • -run โ€” Shorthand for -start -wait. start and wait, together ๐Ÿซถ

In most cases, if you just want to start a process and wait for it to finish, you can use the shell command instead. The following commands are equivalent:

shell "do --something"

process p1 "do --something" -run

However, you may use the process variant to conveniently collect the standard input and output without dealing with shell redirections yourself. The shell command can also expect an expression from either output, consider using it if you only need to match one. iykyk ๐Ÿ’…

  • -key KEYSYM โ€” Send emulated key-press. KEYSYM can be one of (NPAGE, PPAGE, HOME, END). press that key โŒจ๏ธ
  • -kill SIGNAL โ€” Send a signal to the process. The argument can be either the string "TERM", "INT", or "KILL" for SIGTERM, SIGINT or SIGKILL signals, respectively, or a hyphen (-) followed by the signal number. kill it, respectfully ๐Ÿ’€

If you need to use other signal names, you can use the kill(1) command directly:

shell "kill -USR1 ${pNAME_pid}"

Note that SIGHUP usage is discouraged in test cases. don't do it, bestie ๐Ÿ™…

  • -stop โ€” Shorthand for -kill TERM. stop right there โœ‹
  • -close โ€” Alias for "-kill HUP". close but not really ๐Ÿ™ˆ
  • -winsz LINES COLUMNS โ€” Change the terminal window size to LIN lines and COL columns. resize, queen ๐Ÿ“
  • -write STRING โ€” Write a string to the process' stdin. write it out โœ๏ธ
  • -writeln STRING โ€” Same as -write followed by a newline (\n). newline energy ๐Ÿ“
  • -writehex HEXSTRING โ€” Same as -write but interpreted as hexadecimal bytes. hex bytes, fancy ๐Ÿ”ข
  • -need-bytes [+]NUMBER โ€” Wait until at least NUMBER bytes have been received in total. If + is prefixed, NUMBER new bytes must be received. count those bytes ๐Ÿงฎ
  • -ansi-response โ€” Respond to terminal respond-back sequences. ansi, she responds ๐Ÿ’ฌ
  • -expect-cursor LINE COLUMN โ€” Expect cursors location. where's the cursor? ๐Ÿ”
  • -expect-text LINE COLUMNS TEXT โ€” Wait for TEXT to appear at LIN,COL on the virtual screen. Lines and columns are numbered 1...N LIN==0 means "on any line" COL==0 means "anywhere on the line". text hunt ๐Ÿ”Ž
  • -match-text LINE COLUMN REGEXP โ€” Wait for the PAT regular expression to match the text at LIN,COL on the virtual screen. Lines and columns are numbered 1...N LIN==0 means "on any line" COL==0 means "anywhere on the line". regex match, slay โœจ
  • -screen-dump โ€” Dump the virtual screen into vtc_log. dump it ๐Ÿ“ธ

๐ŸŒฑ setenv ๐ŸŒฑ

Set or change an environment variable:

setenv FOO "bar baz"

The above will set the environment variable $FOO to the value provided. There is also an -ifunset argument which will only set the value if the environment variable does not already exist:

setenv -ifunset FOO quux

environmental queen ๐ŸŒ

๐Ÿš shell ๐Ÿš

NOTE: This command is available everywhere commands are given. everywhere, periodt ๐Ÿ’…

Pass the string given as argument to a shell. If you have multiple commands to run, you can use curly brackets to describe a multi-lines script, eg:

shell {
        echo begin
        cat /etc/fstab
        echo end
}

By default a zero exit code is expected, otherwise the vtc will fail. zero or fail, no in-between ๐Ÿ˜ญ

Notice that the commandstring is prefixed with "exec 2>&1;" to combine stderr and stdout back to the test process. stderr + stdout = besties ๐Ÿซถ

Optional arguments:

  • -err โ€” Expect non-zero exit code. error? expected ๐Ÿ’€
  • -exit N โ€” Expect exit code N instead of zero. custom exit code โœ…
  • -expect STRING โ€” Expect string to be found in stdout+err. look for the string ๐Ÿ”
  • -match REGEXP โ€” Expect regexp to match the stdout+err output. regexp match โœจ

๐ŸŒŠ stream ๐ŸŒŠ

(note: this section is at the top-level for easier navigation, but it's part of the client/server specification) iykyk ๐Ÿ“

Streams map roughly to a request in HTTP/2, a request is sent on stream N, the response too, then the stream is discarded. The main exception is the first stream, 0, that serves as coordinator. stream 0 is the coordinator, no cap ๐Ÿ‘‘

Stream syntax follow the client/server one:

stream ID [SPEC] [ACTION]

ID is the HTTP/2 stream number, while SPEC describes what will be done in that stream. If ID has the value next, the actual stream number is computed based on the last one. next means next โžก๏ธ

Note that, when parsing a stream action, if the entity isn't operating in HTTP/2 mode, these spec is ran before:

txpri/rxpri # client/server
stream 0 {
    txsettings
    rxsettings
    txsettings -ack
    rxsettings
    expect settings.ack == true
} -run

And HTTP/2 mode is then activated before parsing the specification. activated, slay โšก

๐ŸŽฌ Actions ๐ŸŽฌ

  • -start โ€” Run the specification in a thread, giving back control immediately. thread go brrr ๐Ÿงต
  • -wait โ€” Wait for the started thread to finish running the spec. wait for it โณ
  • -run โ€” equivalent to calling -start then -wait. start + wait = run ๐Ÿซถ

๐Ÿ“œ Specification ๐Ÿ“œ

The specification of a stream follows the exact same rules as one for a client or a server. same rules, same vibe โœจ

โœ‰๏ธ txreq, txresp, txcont, txpush โœ‰๏ธ

These four commands are about sending headers. txreq and txresp will send HEADER frames; txcont will send CONTINUATION frames; txpush PUSH frames. The only difference between txreq and txresp are the default headers set by each of them. headers, but make it fashion ๐Ÿ’…

  • -noadd โ€” Do not add default headers. Useful to avoid duplicates when sending default headers using -hdr, -idxHdr and -litIdxHdr. no duplicates, bestie ๐Ÿšซ
  • -status INT (txresp) โ€” Set the :status pseudo-header. status check ๐Ÿ“Š
  • -url STRING (txreq, txpush) โ€” Set the :path pseudo-header. path, but make it URL ๐Ÿ”—
  • -method STRING (txreq, txpush) โ€” Set the :method pseudo-header. method to the madness ๐Ÿ› ๏ธ
  • -req STRING (txreq, txpush) โ€” Alias for -method. same thing, different name ๐Ÿ’…
  • -scheme STRING (txreq, txpush) โ€” Set the :scheme pseudo-header. scheme team ๐ŸŒˆ
  • -hdr STRING1 STRING2 โ€” Insert a header, STRING1 being the name, and STRING2 the value. name + value = header โœจ
  • -idxHdr INT โ€” Insert an indexed header, using INT as index. index it ๐Ÿ”ข
  • -litIdxHdr inc|not|never INT huf|plain STRING โ€” Insert an literal, indexed header. The first argument specify if the header should be added to the table, shouldn't, or mustn't be compressed if/when retransmitted.

INT is the index of the header name to use.

The third argument informs about the Huffman encoding: yes (huf) or no (plain).

The last term is the literal value of the header. literal queen ๐Ÿ’–

  • -litHdr inc|not|never huf|plain STRING1 huf|plain STRING2 โ€” Insert a literal header, with the same first argument as -litIdxHdr.

The second and third terms tell what the name of the header is and if it should be Huffman-encoded, while the last two do the same regarding the value. huffman encoding, fancy ๐ŸŒธ

  • -body STRING (txreq, txresp) โ€” Specify a body, effectively putting STRING into a DATA frame after the HEADER frame is sent. body after headers ๐Ÿ“ฆ
  • -bodyfrom FILE (txreq, txresp) โ€” Same as -body but content is read from FILE. read from file ๐Ÿ“
  • -bodylen INT (txreq, txresp) โ€” Do the same thing as -body but generate a string of INT length for you. auto-generate the body ๐Ÿค–
  • -gzipbody STRING (txreq, txresp) โ€” Gzip STRING and send it as body. gzip it and ship it ๐Ÿ—œ๏ธ
  • -gziplen NUMBER (txreq, txresp) โ€” Combine -bodylen and -gzipbody: generate a string of length NUMBER, gzip it and send as body. combo move ๐Ÿ”ฅ
  • -nostrend (txreq, txresp) โ€” Don't set the END_STREAM flag automatically, making the peer expect a body after the headers. no end stream ๐Ÿšซ
  • -nohdrend โ€” Don't set the END_HEADERS flag automatically, making the peer expect more HEADER frames. no end headers ๐Ÿšซ
  • -dep INT (txreq, txresp) โ€” Tell the peer that this content depends on the stream with the INT id. dependency drama ๐Ÿ”—
  • -ex (txreq, txresp) โ€” Make the dependency exclusive (-dep is still needed). exclusive, but need dep ๐Ÿ’…
  • -weight (txreq, txresp) โ€” Set the weight for the dependency. weight it โš–๏ธ
  • -promised INT (txpush) โ€” The id of the promised stream. promised stream, iconic ๐Ÿ‘‘
  • -pad STRING / -padlen INT โ€” ok so this one adds padding to the frame ๐ŸŽ€ either the exact string you handed over with -pad, or one it generates for you โ€” and in the -padlen case that generated string is INT bytes long. Cute, no notes.

โœจ txdata

By default, data frames are empty. The receiving end still knows the whole body landed because of the END_STREAM flag that gets set on the last DATA frame โ€” and txdata sets it for you automatically, bestie. It just does it. ๐Ÿ‘‘

  • -data STRING โ€” the data to be embedded into the frame.
  • -datalen INT โ€” generate a string INT bytes long and send it in the frame.
  • -pad STRING / -padlen INT โ€” same deal as ever: add a padding string to the frame, either the one you gave with -pad, or one generated for you, INT bytes long in the -padlen case.
  • -nostrend โ€” don't set the END_STREAM flag, which lets you send more data on this stream. Live your truth.

๐ŸŒธ rxreq, rxresp

These two are the convenience duo โ€” they receive the headers and the body of an incoming request or response in one go. The only difference between them is that rxreq can only be used by a server, and rxresp only by a client. That's it. That's the whole plot.

๐Ÿฆ‹ rxhdrs

rxhdrs expects one HEADER frame, and then โ€” depending on the arguments โ€” zero or more CONTINUATION frames.

  • -all โ€” keep waiting for CONTINUATION frames until the END_HEADERS flag is seen.
  • -some INT โ€” retrieve INT - 1 CONTINUATION frames after the HEADER frame.

๐Ÿ’œ rxpush

Works exactly like rxhdrs, except it expects a PUSH frame and then zero or more CONTINUATION frames. Same energy, different frame.

  • -all โ€” keep waiting for CONTINUATION frames until the END_HEADERS flag is seen.
  • -some INT โ€” retrieve INT - 1 CONTINUATION frames after the PUSH frame.

๐Ÿ“ rxdata

Receiving data happens through the rxdata keywords, and it'll retrieve one DATA frame. Want more than one? Use these two convenience arguments, bestie:

  • -all โ€” keep waiting for DATA frames until one sets the END_STREAM flag.
  • -some INT โ€” retrieve INT DATA frames.

Receive a frame, any frame. ๐Ÿ’€ (yes that's the whole vibe)

๐Ÿงš sendhex

Push bytes straight onto the wire. sendhex takes exactly one argument: a string describing the bytes in hex notation, with possible whitespaces between them. Obsessed with the simplicity. Here's an example:

sendhex "00 00 08 00 0900       8d"

๐ŸŽ€ rxgoaway

Receive a GOAWAY frame. Short king.

๐ŸŒˆ txgoaway

Possible options include:

  • -err STRING|INT โ€” set the error code to explain the termination. That second argument can be an integer, or the string version of the error code as found in rfc7540#7.
  • -laststream INT โ€” the id of the "highest-numbered stream identifier for which the sender of the GOAWAY frame might have taken some action on or might yet take action on". (Yes, that's the quote. It's giving RFC.)
  • -debug โ€” specify the debug data, if any, to append to the frame.

๐Ÿซถ gunzip

Same as the gunzip command for HTTP/1.

โญ rxping

Receive a PING frame.

๐Ÿ’… txping

Send a PING frame.

  • -data STRING โ€” specify the payload of the frame, with STRING being an 8-char string.
  • -ack โ€” set the ACK flag.

๐Ÿ”ฅ rxprio

Receive a PRIORITY frame.

๐Ÿ‘‘ txprio

Send a PRIORITY frame.

  • -stream INT โ€” indicate the id of the stream the sender stream depends on.
  • -ex โ€” the dependency should be made exclusive (only this stream depends on the parent stream).
  • -weight INT โ€” an 8-bit integer, used to balance priority between streams that depend on the same streams.

๐Ÿ˜ญ rxrst

Receive a RST_STREAM frame.

๐Ÿ™ˆ txrst

Send a RST_STREAM frame. By default, txrst sends a 0 error code (NO_ERROR). Chill of it.

  • -err STRING|INT โ€” sets the error code to be sent. The argument can be an integer, or a string describing the error such as NO_ERROR, or CANCEL (see rfc7540#11.4 for more strings).

๐Ÿฆ‹ rxsettings

Receive a SETTINGS frame.

๐ŸŒธ txsettings

SETTINGS frames must be acknowledged, and the arguments are as follow (most of them are straight from rfc7540#6.5.2):

  • -hdrtbl INT โ€” headers table size
  • -push BOOL โ€” whether push frames are accepted or not
  • -maxstreams INT โ€” maximum concurrent streams allowed
  • -winsize INT โ€” sender's initial window size
  • -framesize INT โ€” largest frame size authorized
  • -hdrsize INT โ€” maximum size of the header list authorized
  • -0xHH[HH] INT โ€” tx arbitrary settings with tag xx
  • -ack โ€” set the ack bit

๐Ÿ’œ rxwinup

Receive a WINDOW_UPDATE frame.

๐ŸŽ€ txwinup

Transmit a WINDOW_UPDATE frame, increasing the amount of credit of the connection (from stream 0) or of the stream (any other stream).

  • -size INT โ€” give INT credits to the peer.

write_body STRING โ€” same as the write_body command for HTTP/1.

๐Ÿงš expect

expect inside a stream works just like it does in a client or server, except the elements being compared are different. Most of these elements are frame specific โ€” meaning the last frame received on that stream must be of the correct type. Here's the list of keywords you can look at ๐Ÿ‘€

โญ GOAWAY specific

  • goaway.err โ€” the error code (as an integer) of the GOAWAY frame.
  • goaway.laststream โ€” Last-Stream-ID
  • goaway.debug โ€” debug data, if any.

๐Ÿ’… PING specific

  • ping.data โ€” the 8-bytes string of the PING frame payload.
  • ping.ack (PING) โ€” "true" if the ACK flag was set, "false" otherwise.

๐Ÿ”ฅ PRIORITY specific

  • prio.stream โ€” the stream ID announced.
  • prio.exclusive โ€” "true" if the priority is exclusive, else "false".
  • prio.weight โ€” the dependency weight.

๐Ÿ‘‘ PUSH_PROMISE specific

  • push.id โ€” the id of the promised stream.

๐Ÿ˜ญ RESET_STREAM specific

  • rst.err โ€” the error code (as an integer) of the RESET_STREAM frame.

๐Ÿ™ˆ SETTINGS specific

  • settings.ack โ€” "true" if the ACK flag was set, else "false".
  • settings.push โ€” "true" if the push settings was set to yes, "false" if set to no, and <undef> if not present.
  • settings.hdrtbl โ€” value of HEADER_TABLE_SIZE if set, <undef> otherwise.
  • settings.maxstreams โ€” value of MAX_CONCURRENT_STREAMS if set, <undef> otherwise.
  • settings.winsize โ€” value of INITIAL_WINDOW_SIZE if set, <undef> otherwise.
  • setting.framesize โ€” value of MAX_FRAME_SIZE if set, <undef> otherwise.
  • settings.hdrsize โ€” value of MAX_HEADER_LIST_SIZE if set, <undef> otherwise.

๐Ÿฆ‹ WINDOW_UPDATE specific

  • winup.size โ€” the size of the upgrade given by the WINDOW_UPDATE frame.

๐ŸŒˆ Generic frame

  • frame.data โ€” payload of the last frame
  • frame.type โ€” type of the frame, as integer.
  • frame.size โ€” size of the frame.
  • frame.stream โ€” stream of the frame (corresponds to the one you're executing this from, obviously).
  • frame.padding (for DATA, HEADERS, PUSH_PROMISE frames) โ€” number of padded bytes.

๐Ÿซถ Request and response

Note: it's possible to inspect a request or response while it is still being constructed (in-between two frames, for example).
  • req.bodylen / resp.bodylen โ€” length in bytes of the request/response so far.
  • req.body / resp.body โ€” body of the request/response so far.
  • req.http.STRING / resp.http.STRING โ€” value of the header STRING in the request/response.
  • req.status / resp.status โ€” :status pseudo-header's value.
  • req.url / resp.url โ€” :path pseudo-header's value.
  • req.method / resp.method โ€” :method pseudo-header's value.
  • req.authority / resp.authority โ€” :method pseudo-header's value.
  • req.scheme / resp.scheme โ€” :method pseudo-header's value.

โญ Stream

  • stream.window โ€” the current local window size of the stream, or, if on stream 0, of the connection.
  • stream.peer_window โ€” the current peer window size of the stream, or, if on stream 0, of the connection.
  • stream.weight โ€” weight of the stream
  • stream.dependency โ€” id of the stream this one depends on.

๐Ÿ’œ Index tables

  • tbl.dec.size / tbl.enc.size โ€” size (bytes) of the decoding/encoding table.
  • tbl.dec.size / tbl.enc.maxsize โ€” maximum size (bytes) of the decoding/encoding table.
  • tbl.dec.length / tbl.enc.length โ€” number of headers in decoding/encoding table.
  • tbl.dec[INT].key / tbl.enc[INT].key โ€” name of the header at index INT of the decoding/encoding table.
  • tbl.dec[INT].value / tbl.enc[INT].value โ€” value of the header at index INT of the decoding/encoding table.

๐ŸŽ€ syslog

Define and interact with syslog instances (for use with haproxy).

To define a syslog server, you'll use this syntax:

syslog SNAME

Arguments:

  • SNAME โ€” identify the syslog server with a string which must start with 'S'.
  • -level STRING โ€” set the default syslog priority level used by any subsequent "recv" command. Any syslog dgram with a different level will be skipped by the "recv" command. This default level value may be superseded by the "recv" command if supplied as first argument: "recv <level>".
  • -start โ€” start the syslog server thread in the background.
  • -repeat โ€” instead of processing the specification only once, do it NUMBER times.
  • -bind โ€” bind the syslog socket to a local address.
  • -wait โ€” wait for that thread to terminate.
  • -stop โ€” stop the syslog server thread.

๐Ÿฆ‹ tunnel

The goal of a tunnel is to help control the data transfer between two parties โ€” for example, to trigger socket timeouts in the middle of protocol frames, without needing to change how both parties are implemented. Big brain move, honestly.

A tunnel accepts a connection and then connects on behalf of the source to the desired destination. Once both connections are established, the tunnel transfers bytes unchanged between the source and destination. That transfer can be interrupted, usually with the help of synchronization methods like barriers. Once the transfer is paused, it's possible to let a specific amount of bytes move in either direction. Chefs kiss.

โœจ Arguments

  • -start โ€” start the tunnel in background, processing the last given specification.
  • -start+pause โ€” start the tunnel, but already paused.
  • -wait โ€” block until the thread finishes.
  • -listen STRING โ€” dictate the listening socket for the server. STRING is of the form "IP PORT", or "HOST PORT".

Listens by default to a local random port.

  • -connect STRING โ€” indicate the server to connect to. STRING is also of the form "IP PORT", or "HOST PORT".

Connects by default to a varnish instance called v1.

๐ŸŒธ Specification

The specification contains a list of tunnel commands that can be combined with barriers and delays. For example:

          tunnel t1 {
              barrier b1 sync
              pause
              delay 1
              send 42
              barrier b2 sync
              resume
          } -start

okay so if one end of the tunnel gets closed before the spec is over, bestie, the whole test case just fails. ๐Ÿ’€ and if a spec ends while it's still paused, the tunnel gets implicitly resumed. no cap.

pause ๐ŸŒธ

Wait for all the in-flight bytes to actually transfer, then pause the tunnel. ๐Ÿ’…

the tunnel has to be running for this to work. don't come at it paused, it's not the vibe.

recv NUMBER ๐Ÿฆ‹

Wait until NUMBER bytes make it from destination to source.

tunnel must be paused, and it stays paused after. ๐Ÿซถ

resume โœจ

Resume moving bytes in both directions.

tunnel must be paused first, bestie.

send NUMBER ๐Ÿ’Œ

Wait until NUMBER bytes go from source to destination.

tunnel must be paused, and yeah it remains paused afterwards. ๐ŸŽ€

varnish ๐Ÿ‘‘

Define varnish instances and talk to them. the main character section fr.

To define a Varnish server, you use this syntax:

   varnish vNAME [-arg STRING] [-vcl STRING] [-vcl+backend STRING]
           [-errvcl STRING STRING] [-jail STRING] [-proto PROXY]

The very first varnish vNAME invocation kicks off the varnishd master process in the background, and it just... waits there for the -start switch to actually start the child. iconic behaviour tbh.

Types used in the description below:

PATTERN โ€” is a 'glob' style pattern (ie: fnmatch(3)) as used in shell filename expansion. yes bestie, like your shell does. ๐Ÿ’…

Arguments:

  • vNAME โ€” identify the Varnish server with a string, and it must start with 'v'. rules are rules. ๐ŸŽ€
  • -arg STRING โ€” pass an argument straight to varnishd, for example "-h simple_list".

and listen: if the ${varnishd_args_prepend} or ${varnishd_args_append} macros are defined, they get expanded and inserted before / appended to the varnishd command line as constructed by varnishtest, before the command line itself is expanded. this is the little cheat code that lets you tweak the varnishd command line without editing test cases. you define these macros with the -D option for varnishtest. iykyk. ๐Ÿ”ฅ

  • -vcl STRING โ€” specify the VCL to load on this Varnish instance. you're probably gonna want multi-line strings for this one, like ({...}). ๐Ÿ’–
  • -vcl+backend STRING โ€” does the exact same thing as -vcl, but it also adds the definition block of known backends (ie. the ones already defined). slay.
  • -errvcl STRING1 STRING2 โ€” loads STRING2 as VCL, expects it to fail, and expects Varnish to send an error string matching STRING1. ๐Ÿ’€
  • -jail STRING โ€” go look at man varnishd (-j) for more info, bestie.
  • -proto PROXY โ€” makes Varnish use the proxy protocol. note that PROXY here is the actual string. not a placeholder, the literal vibe.

You can also decide to start the Varnish instance and/or wait for a bunch of events:

   varnish vNAME [-start] [-wait] [-wait-running] [-wait-stopped]
  • -start โ€” start the child process. ๐ŸŒˆ

once it's successfully started, these macros become available for the default listen address: ${vNAME_addr}, ${vNAME_port} and ${vNAME_sock}. and there's more โ€” including the listen address name for each address vNAME listens to, like for example: ${vNAME_a0_addr}. so many macros, we love to see it. โœจ

  • -stop โ€” stop the child process.
  • -syntax โ€” set the VCL syntax level for this command (default: 4.1). ๐Ÿ“Œ
  • -wait โ€” wait for that instance to terminate.
  • -wait-running โ€” wait for the Varnish child process to be started.
  • -wait-stopped โ€” wait for the Varnish child process to stop.
  • -cleanup โ€” once Varnish is stopped, clean everything after it. heads up: this is only used in very few tests and you should never need it. like, seriously.
  • -expectexit NUMBER โ€” expect varnishd to exit(3) with this value.

Once Varnish is started, you can talk to it โ€” exactly like you would through varnishadm โ€” with these extra switches:

   varnish vNAME [-cli STRING] [-cliok STRING] [-clierr STRING]
                 [-clijson STRING]
  • -cli STRING | -cliok STRING | -clierr STATUS STRING | -cliexpect [!] REGEXP STRING โ€” all four of these send STRING to the CLI. the only difference is what they expect back. -cli doesn't expect anything at all (chill), -cliok expects 200, -clierr expects STATUS, and -cliexpect expects the REGEXP to match the returned response. and -cliexpect ! ... negates the match. ๐Ÿ’…
  • -clijson STRING โ€” send STRING to the CLI, expect success (CLIS_OK/200) and check that the response is parsable JSON. โœ…

You can also interact with its shared memory โ€” like you would through tools such as varnishstat โ€” with these additional switches:

  • -expect !PATTERN|PATTERN OP NUMBER|PATTERN OP PATTERN โ€” peek into the VSM and make sure the first VSC counter identified by PATTERN has a correct value. OP can be ==, >, >=, <, <=. for example:
   varnish v1 -expect SM?.s1.g_space > 1000000
   varnish v1 -expect cache_hit >= cache_hit_grace

in the ! form, the test fails if a counter matches PATTERN. ๐Ÿ™ˆ

the MAIN. namespace can be omitted from PATTERN. ๐Ÿซถ

and the test takes up to 5 seconds before timing out. so don't hold your breath, bestie.

  • -vsc PATTERN โ€” dump VSC counters matching PATTERN.
  • -vsl_catchup โ€” wait until the logging thread has idled, so you know all the generated log is flushed. ๐Ÿ’œ

varnishtest ๐Ÿงš

alternate name for 'vtest', see above. โœจ

vsm ๐Ÿฆ‹

interact with the shared memory of a varnish instance.

to define a VSM consumer, use this syntax:

   vsm mNAME [-n STRING]

Arguments:

  • mNAME โ€” identify the VSM consumer, and it must start with 'm'. ๐ŸŽ€
  • -n STRING โ€” choose the working directory of the varnish instance. by default a VSM consumer connects to ${v1_name}.
  • -attach โ€” attach to a new varnish instance. it implicitly detaches from the current varnish instance if that applies. ๐Ÿซถ
  • -detach โ€” detach from the current varnish instance.
  • -expect-status STRING โ€” check that the status of the VSM matches the list of status flags from STRING. the expected status is written as a comma-separated list of flags, and the list in STRING is not sensitive to the order of the flags. order? doesn't matter, we don't gatekeep. ๐Ÿ’…

the available flags are:

  • mgt-running
  • mgt-changed
  • mgt-restarted
  • wrk-running
  • wrk-changed
  • wrk-restarted

expecting a status automatically attaches to the varnish instance if that wasn't already the case. no cap. โœจ

vtest ๐ŸŒธ

this should be the first command in your vtc, because it identifies the test case with a short but descriptive sentence. it takes exactly one argument, a string, eg:

   vtest "Check that vtest is actually a valid command"

and yeah, it also prints that string in the log. ๐Ÿ’–


๐Ÿ“œ HISTORY

this document has been written by Guillaume Quintard. thank you legend. ๐Ÿ‘‘


๐Ÿ‘€ SEE ALSO


this document is licensed under the same licence as Varnish itself. see LICENCE for details.

  • Copyright (c) 2006-2016 Varnish Software AS

and that's a wrap on VTC(7), bestie!! ๐ŸŽ€๐Ÿฆ‹โœจ you read the whole thing, that's genuinely iconic of you. go forth and write some unhinged test cases. mwah. ๐Ÿ’…๐Ÿ’–

(psst โ€” this was the VTC(7) page from the Varnish source tree, dated 2006-2016 territory, brought to you by Varnish Software AS. okay bye!! ๐ŸŒˆ)

๐Ÿ“ฆ where this page comes from
  • fedora 44: varnish 8.0.2-1.fc44 ยท BSD-2-Clause AND (BSD-2-Clause-FreeBSD AND BSD-3-Clause AND LicenseRef-Fedora-Public-Domain AND Zlib)
  • fedora 44: varnish 8.0.2-1.fc44 ยท BSD-2-Clause AND (BSD-2-Clause-FreeBSD AND BSD-3-Clause AND LicenseRef-Fedora-Public-Domain AND Zlib)
  • ๐Ÿ“œ the page's license: BSD-2-Clause
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms