Skip to content

Instantly share code, notes, and snippets.

@bukzor
Last active April 8, 2024 17:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bukzor/2023fe0f7d8a0a500f5b0bd028c2b6f0 to your computer and use it in GitHub Desktop.
Save bukzor/2023fe0f7d8a0a500f5b0bd028c2b6f0 to your computer and use it in GitHub Desktop.
[
{
"name": "metadata",
"description": "test metadata, from test_metadata.py",
"fields": [
{
"name": "cwd",
"description": "current working directory",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "host",
"description": "fully-qualified hostname",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "env",
"description": "select environment variables -- especially PWD, HOME, USER, and GITHUB_*",
"type": "JSON",
"mode": "REQUIRED"
},
{
"name": "home",
"description": "user's home directory",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "username",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "uid",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "group",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "gid",
"type": "INTEGER",
"mode": "NULLABLE"
}
]
},
{
"name": "test-data",
"description": "raw data collected from pytest-json-report plugin",
"fields": [
{
"name": "created",
"description": "Report creation date. (Unix time)",
"type": "FLOAT",
"mode": "REQUIRED"
},
{
"name": "duration",
"description": "Session duration in seconds.",
"type": "FLOAT",
"mode": "REQUIRED"
},
{
"name": "exitcode",
"description": "Process exit code as listed in the pytest docs. The exit code is a quick way to tell if any tests failed, an internal error occurred, etc.",
"type": "INTEGER",
"mode": "REQUIRED"
},
{
"name": "root",
"description": "Absolute root path from which the session was started.",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "summary",
"description": "Number of outcomes per category and the total number of test items.",
"type": "RECORD",
"mode": "REQUIRED",
"fields": [
{
"name": "collected",
"description": "Total number of tests collected.",
"type": "INTEGER",
"mode": "REQUIRED"
},
{
"name": "passed",
"description": "(absent if number is zero)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "failed",
"description": "(absent if number is zero)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "xfailed",
"description": "(absent if number is zero)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "xpassed",
"description": "(absent if number is zero)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "error",
"description": "(absent if number is zero)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "skipped",
"description": "(absent if number is zero)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "total",
"description": "Total number of tests run.",
"type": "INTEGER",
"mode": "REQUIRED"
}
]
},
{
"name": "environment",
"description": "The environment section is provided by pytest-metadata. All metadata given by that plugin will be added here, so you need to make sure it is JSON-serializable.",
"type": "RECORD",
"mode": "REQUIRED",
"fields": []
},
{
"name": "collectors",
"description": "A list of collector nodes. These are useful to check what tests are available without running them, or to debug an error during test discovery.",
"type": "RECORD",
"mode": "REPEATED",
"fields": [
{
"name": "nodeid",
"description": "ID of the collector node. (See docs) The root node has an empty node ID.",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "outcome",
"description": "Outcome of the collection. (Not the test outcome!)",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "result",
"description": "Nodes collected by the collector.",
"type": "RECORD",
"mode": "REPEATED",
"fields": [
{
"name": "nodeid",
"description": "ID of the node.",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "type",
"description": "Type of the collected node.",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "lineno",
"description": "Line number. (absent if not applicable)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "deselected",
"description": "true if the test is deselected. (absent if not deselected)",
"type": "BOOL",
"mode": "NULLABLE"
}
]
},
{
"name": "longrepr",
"description": "Representation of the collection error. (absent if no error occurred)",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "tests",
"description": "A list of test nodes. Each completed test stage produces a stage object (setup, call, teardown) with its own outcome.",
"type": "RECORD",
"mode": "REPEATED",
"fields": [
{
"name": "nodeid",
"description": "ID of the test node.",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "lineno",
"description": "Line number where the test starts.",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "keywords",
"description": "List of keywords and markers associated with the test.",
"type": "STRING",
"mode": "REPEATED"
},
{
"name": "outcome",
"description": "Outcome of the test run.",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "setup",
"description": "Test stage entry. To find the error in a failed test you need to check all stages. (absent if stage didn't run)",
"type": "RECORD",
"mode": "NULLABLE",
"fields": [
{
"name": "duration",
"description": "Duration of the test stage in seconds.",
"type": "FLOAT",
"mode": "REQUIRED"
},
{
"name": "outcome",
"description": "Outcome of the test stage. (can be different from the overall test outcome)",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "crash",
"description": "Crash entry. (absent if no error occurred)",
"type": "RECORD",
"mode": "NULLABLE",
"fields": [
{
"name": "path",
"description": "path associated with the stack frame",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "lineno",
"description": "Line number. (absent if not applicable)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "message",
"description": "message associated with the stack frame",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "traceback",
"description": "List of traceback entries. (absent if no error occurred; affected by --tb option)",
"type": "RECORD",
"mode": "REPEATED",
"fields": [
{
"name": "path",
"description": "path associated with the stack frame",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "lineno",
"description": "Line number. (absent if not applicable)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "message",
"description": "message associated with the stack frame",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "stdout",
"description": "Standard output. (absent if none available)",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "stderr",
"description": "Standard error. (absent if none available)",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "log",
"description": "Log entry. (absent if none available)",
"type": "JSON",
"mode": "NULLABLE"
},
{
"name": "longrepr",
"description": "Representation of the error. (absent if no error occurred; format affected by --tb option)",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "call",
"description": "Test stage entry. To find the error in a failed test you need to check all stages. (absent if stage didn't run)",
"type": "RECORD",
"mode": "NULLABLE",
"fields": [
{
"name": "duration",
"description": "Duration of the test stage in seconds.",
"type": "FLOAT",
"mode": "REQUIRED"
},
{
"name": "outcome",
"description": "Outcome of the test stage. (can be different from the overall test outcome)",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "crash",
"description": "Crash entry. (absent if no error occurred)",
"type": "RECORD",
"mode": "NULLABLE",
"fields": [
{
"name": "path",
"description": "path associated with the stack frame",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "lineno",
"description": "Line number. (absent if not applicable)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "message",
"description": "message associated with the stack frame",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "traceback",
"description": "List of traceback entries. (absent if no error occurred; affected by --tb option)",
"type": "RECORD",
"mode": "REPEATED",
"fields": [
{
"name": "path",
"description": "path associated with the stack frame",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "lineno",
"description": "Line number. (absent if not applicable)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "message",
"description": "message associated with the stack frame",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "stdout",
"description": "Standard output. (absent if none available)",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "stderr",
"description": "Standard error. (absent if none available)",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "log",
"description": "Log entry. (absent if none available)",
"type": "JSON",
"mode": "NULLABLE"
},
{
"name": "longrepr",
"description": "Representation of the error. (absent if no error occurred; format affected by --tb option)",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "teardown",
"description": "Test stage entry. To find the error in a failed test you need to check all stages. (absent if stage didn't run)",
"type": "RECORD",
"mode": "NULLABLE",
"fields": [
{
"name": "duration",
"description": "Duration of the test stage in seconds.",
"type": "FLOAT",
"mode": "REQUIRED"
},
{
"name": "outcome",
"description": "Outcome of the test stage. (can be different from the overall test outcome)",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "crash",
"description": "Crash entry. (absent if no error occurred)",
"type": "RECORD",
"mode": "NULLABLE",
"fields": [
{
"name": "path",
"description": "path associated with the stack frame",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "lineno",
"description": "Line number. (absent if not applicable)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "message",
"description": "message associated with the stack frame",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "traceback",
"description": "List of traceback entries. (absent if no error occurred; affected by --tb option)",
"type": "RECORD",
"mode": "REPEATED",
"fields": [
{
"name": "path",
"description": "path associated with the stack frame",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "lineno",
"description": "Line number. (absent if not applicable)",
"type": "INTEGER",
"mode": "NULLABLE"
},
{
"name": "message",
"description": "message associated with the stack frame",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "stdout",
"description": "Standard output. (absent if none available)",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "stderr",
"description": "Standard error. (absent if none available)",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "log",
"description": "Log entry. (absent if none available)",
"type": "JSON",
"mode": "NULLABLE"
},
{
"name": "longrepr",
"description": "Representation of the error. (absent if no error occurred; format affected by --tb option)",
"type": "STRING",
"mode": "NULLABLE"
}
]
},
{
"name": "metadata",
"description": "Metadata item. (absent if no metadata)",
"type": "JSON",
"mode": "NULLABLE"
}
]
}
]
}
]
{
"cwd": "/Users/buck/repo/buckevan-tmp-2024-03-29--pytest-data-collection",
"host": "jnpyxr75kj.lan",
"uid": 501,
"username": "buck",
"home": "/Users/buck",
"gid": 20,
"group": "staff",
"env": {
"CAML_LD_LIBRARY_PATH": "/Users/buck/.opam/default/lib/stublibs:/Users/buck/.opam/default/lib/ocaml/stublibs:/Users/buck/.opam/default/lib/ocaml",
"GEM_HOME": "/Users/buck/libxec/rubygems",
"GOPATH": "/Users/buck/libexec/gopath",
"HOME": "/Users/buck",
"HOMEBREW": "/opt/homebrew/bin/brew",
"HOMEBREW_CELLAR": "/opt/homebrew/Cellar",
"HOMEBREW_DEVELOPER": "1",
"HOMEBREW_EVAL_ALL": "1",
"HOMEBREW_MAKE_JOBS": "30",
"HOMEBREW_PREFIX": "/opt/homebrew",
"HOMEBREW_REPOSITORY": "/opt/homebrew",
"HOMEBREW_VERBOSE": "1",
"INFOPATH": "/opt/homebrew/share/info:/opt/homebrew/libexec/gnuinfo:/opt/homebrew/share/info:/opt/homebrew/libexec/gnuinfo:",
"LOGNAME": "buck",
"MANPATH": "/opt/homebrew/share/man:/opt/homebrew/libexec/gnuman:/usr/share/man:/usr/local/share/man:/opt/homebrew/share/man:/opt/homebrew/libexec/gnuman:/Applications/kitty.app/Contents/Resources/man:",
"OCAML_TOPLEVEL_PATH": "/Users/buck/.opam/default/lib/toplevel",
"PATH": "/Users/buck/.pyenv/versions/3.12.1/bin:/opt/homebrew/Cellar/pyenv/2.3.36/libexec:/opt/homebrew/Cellar/pyenv/2.3.36/plugins/python-build/bin:/Users/buck/.opam/default/bin:/Users/buck/.pyenv/shims:/Users/buck/.volta/bin:/Users/buck/.local/bin:/Users/buck/private-dotfiles/bin:/Users/buck/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/opt/homebrew/libexec/gnubin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Library/Apple/usr/bin:/opt/homebrew/bin:/Applications/VMware Fusion.app/Contents/Public:/opt/salt:/Users/buck/.cargo/bin:/Applications/kitty.app/Contents/MacOS:/Users/buck/libexec/pnpm:/Users/buck/.local/share/sentry-devenv/bin",
"PNPM_HOME": "/Users/buck/libexec/pnpm",
"PWD": "/Users/buck/repo/buckevan-tmp-2024-03-29--pytest-data-collection",
"PYENV_HOOK_PATH": "/Users/buck/.pyenv/pyenv.d:/opt/homebrew/Cellar/pyenv/2.3.36/pyenv.d:/opt/homebrew/etc/pyenv.d:/etc/pyenv.d:/usr/lib/pyenv/hooks",
"PYTHONBREAKPOINT": "pudb.set_trace",
"PYTHONPATH": "/Users/buck/repo/buckevan-tmp-2024-03-29--pytest-data-collection/lib/python:/Users/buck/lib/python:/Users/buck/lib/python",
"PYTHONSTARTUP": "/Users/buck/.pythonrc.py",
"TMPDIR": "/var/folders/cc/xr5l1jgd4tz3w1d7tvkwq5sw0000gn/T",
"USER": "buck",
"VOLTA_HOME": "/Users/buck/.volta",
"XDG_CACHE_HOME": "/Users/buck/.cache",
"XDG_CONFIG_DIRS": "/etc/xdg",
"XDG_CONFIG_HOME": "/Users/buck/.config",
"XDG_DATA_DIRS": "/usr/local/share/:/usr/share/",
"XDG_DATA_HOME": "/Users/buck/.local/share",
"XDG_RUNTIME_DIR": "/var/folders/cc/xr5l1jgd4tz3w1d7tvkwq5sw0000gn/T/runtime-buck-1711903691",
"__orig_PATH": "/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Library/Apple/usr/bin:/opt/homebrew/bin:/Applications/VMware Fusion.app/Contents/Public:/opt/salt:/Users/buck/.cargo/bin:/Applications/kitty.app/Contents/MacOS"
}
}
#!/usr/bin/env python3
import grp
import os
import pwd
import socket
Environ = dict[str, str]
def relevant_env_var(var: str) -> bool:
# prefixes and suffixes of environment vars that we may want during
# debugging, test-result analysis, or traceback normalization
return var.endswith(("HOME", "PATH")) or var.startswith(
(
"USER",
"HOME",
"HOST",
"XDG",
"LOG",
"PWD",
"TMP",
"PYTHON",
"GITHUB",
"RUNNER",
"CI",
)
)
def get_env(env: Environ) -> Environ:
"""Keep the relevant parts of the environment variables."""
relevant_vars = sorted(var for var in env if relevant_env_var(var))
return {var: env[var] for var in relevant_vars}
def test_metadata():
uid = os.getuid()
gid = os.getgid()
pw = pwd.getpwuid(uid)
gr = grp.getgrgid(gid)
return {
"cwd": os.getcwd(),
"host": socket.getfqdn(),
"uid": uid,
"username": pw.pw_name,
"home": pw.pw_dir,
"gid": gid,
"group": gr.gr_name,
"env": get_env(dict(os.environ)),
}
def main():
import json
print(json.dumps(test_metadata(), indent=2))
if __name__ == "__main__":
raise SystemExit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment