conformance

package module
v0.0.0-...-ed96713 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 27 Imported by: 0

README

Conformance harness

Captures the observable error behaviour of Cloud Tasks and validates the emulator against it. One battery of deliberately-malformed RPCs runs against either target through the official Cloud Tasks client (cloud.google.com/go/cloudtasks/apiv2) — so what we record is exactly what a real caller using the SDK sees (routing headers, deadlines and all), not a reconstruction.

This is a separate Go module (test/conformance/go.mod) so it can depend on the current client without disturbing the emulator's intentionally-pinned dependency graph. Run all commands below from inside the test/conformance/ directory (or with go -C test/conformance ...).

One battery of deliberately-malformed RPCs runs against either target:

  • against real Cloud Tasks → committed golden snapshot (golden/errors.json)
  • against the emulator → diffed against the golden by the conformance test

This is what mapErr (and its per-handler variants in protohelpers.go) should be derived from, replacing the hand-guessed messages whose comments flag them as unverified.

Why templates, not literal messages

Cloud Tasks interpolates request values into some messages (e.g. the queue/task-name-mismatch error embeds both names). Each case therefore runs with several variants whose queue/task IDs differ. Normalize replaces those known input substrings with placeholders ({queue_path}, {task_id}, …). The resulting template is what we store and diff.

If a variant value leaks through normalization, the variants disagree and the case is flagged unstable — that means there is an interpolated slot we haven't modeled yet. Add a replacement in normalize.go (or vary that input) until it's stable, then trust the template.

Record the golden from real Cloud Tasks

Needs a throwaway GCP project with the Cloud Tasks API enabled. Control-plane calls only (no task dispatch) — comfortably within the free tier.

gcloud auth application-default login \
  --scopes=https://www.googleapis.com/auth/cloud-platform,openid,https://www.googleapis.com/auth/userinfo.email
gcloud services enable cloudtasks.googleapis.com --project $PROJECT

cd test/conformance
go run ./cmd/record \
  -target=real -project=$PROJECT -location=us-central1 \
  -out=golden/errors.json

Resource names are run-scoped (random prefix) and cases clean up after themselves, so re-runs don't collide. Watch stderr for UNSTABLE lines before committing the snapshot.

Validate the emulator

cd test/conformance
go test -tags conformance ./...

The test builds and starts the emulator (from the parent module) on a free port, replays the battery, and reports every status code, message template or error detail that differs from the golden. It skips if no golden snapshot is present.

Error details (the *errdetails.* payloads Cloud Tasks attaches to some errors, e.g. a Help link on an invalid-name InvalidArgument) are compared as part of the contract. Their text is prototext, whose field separator is deliberately unstable, so the comparison collapses whitespace before diffing.

To eyeball the emulator's current behaviour without a golden:

go run ./cmd/emulator -port 8123 &                                  # from repo root
cd test/conformance
go run ./cmd/record -target=emulator -addr=localhost:8123 -out=/tmp/emu.json

Adding cases

Append to Cases() in cases.go. Each case names the RPC under test, an error category, and an Invoke; use Setup/Teardown for preconditions (e.g. create-then-delete to reach a "recently deleted" state). Names are golden keys — don't rename casually.

Known divergences

knowndiff.go is the explicit ledger of cases where the emulator is knowingly unfaithful for reasons beyond message mapping (real behaviour gaps we've deferred). The validation test reports these as KNOWN instead of failing, and fails if one starts matching (so the entry gets removed). Current entries:

  • queue/create/invalid-parent — real resolves any parent string to a project and returns PermissionDenied via IAM; the emulator has no project/IAM concept and returns InvalidArgument. Not reproducible by design.

Happy-path battery

A second, separate battery captures a success-response shape rather than an error: what Cloud Tasks echoes back after a task is created and read. It exists to settle behaviour the proto docs leave ambiguous and that issues #111/#53 turn on:

  • Key casing at rest - is a submitted content-type stored verbatim or canonicalized to Content-Type?
  • Default Content-Type - for an AppEngine task with a body, does the application/octet-stream default appear in the stored task, or only on the dispatched wire request? This decides whether the emulator should inject it at rest or at dispatch.
  • View sensitivity - which fields does the BASIC response view withhold? The body is documented as omitted under BASIC (forcing FULL), while headers are returned under both. Each stage captures both, so the golden records the real division.

Each observation creates a task carrying a lowercase content-type and a mixed-case custom header plus a body, then reads it back via CreateTask (FULL), GetTask (BASIC) and GetTask (FULL), capturing the headers and body at each stage. See snapshot.go.

Record it against real Cloud Tasks. FULL view requires the cloudtasks.tasks.fullView IAM permission on the queue (owner/editor have it):

cd test/conformance
go run ./cmd/record \
  -target=real -kind=happypath -project=$PROJECT -location=us-central1 \
  -out=golden/happypath.json

TestEmulatorErrors's sibling TestEmulatorHappyPath diffs the emulator against golden/happypath.json (and skips if it's absent). This battery is control-plane only; the headers Cloud Tasks puts on the wire when it dispatches a task are covered by the dispatch battery below.

Dispatch-headers battery

A third battery captures what Cloud Tasks puts on the wire when it dispatches - and re-dispatches - a task, rather than what it stores. Its purpose is the two optional retry headers whose value format is documented nowhere: X-CloudTasks-TaskPreviousResponse / X-CloudTasks-TaskRetryReason and their X-AppEngine-* equivalents. They appear only on the dispatch request, and only after a task has already failed once, so no control-plane call can observe them.

The battery creates a task pointed at a small receiver (see receiver/) that fails each attempt with a different status - 503, 404, 429, 500, 302 - before succeeding, then reads back the per-attempt headers the receiver recorded. Failing across a range of codes captures the optional headers for each, since the reason may differ by prior status. Separate timeout cases (dispatch/http-timeout, dispatch/appengine-timeout) instead stall the first attempt past its dispatch deadline, capturing what a no-response failure (rather than an error status) produces on the retry (real Cloud Tasks enforces a per-task dispatch deadline on the App Engine path too, reporting the timeout as Instance Unavailable). See dispatch.go.

The receiver is deployed to App Engine (not tunnelled via ngrok) because that is the only vantage point that can observe the X-AppEngine-* retry headers: App Engine-target tasks route through internal App Engine routing that cannot be pointed at a tunnel. One deployed app covers both families - an HTTP-target task points its URL at https://PROJECT.appspot.com/recv/http, an App Engine-target task routes to /recv/appengine. See receiver/README.md for the project setup and deploy steps.

Recording (real) vs validating (emulator)

The same receiver handler and the same battery drive both flows; only the target differs. The golden is recorded once against real Cloud Tasks, then every validation run diffs the emulator against it - no GCP involved.

Recording the golden - the App Engine app is the dispatch target real Cloud Tasks delivers to:

sequenceDiagram
    autonumber
    participant REC as cmd/record (real)
    participant CT as Cloud Tasks (real)
    participant RCV as Receiver (App Engine)
    REC->>CT: CreateQueue (fast retry) + CreateTask (targets receiver)
    loop forced failures (503, 404, 429, 500, 302)
        CT->>RCV: dispatch (retry)
        RCV-->>CT: non-2xx (forced fail)
    end
    CT->>RCV: dispatch (final retry)
    RCV-->>CT: 200 (success)
    REC->>RCV: GET /captures?run=PREFIX
    RCV-->>REC: recorded per-attempt headers
    Note over REC: write golden/dispatch.json

Validating the emulator - a local receiver stands in for the App Engine app, and APP_ENGINE_EMULATOR_HOST makes the emulator's App Engine tasks reach it too:

sequenceDiagram
    autonumber
    participant T as TestEmulatorDispatch
    participant EMU as Emulator
    participant RCV as Receiver (local httptest)
    Note over T,RCV: APP_ENGINE_EMULATOR_HOST points at the local receiver
    T->>EMU: CreateQueue (fast retry) + CreateTask (targets receiver)
    loop forced failures (503, 404, 429, 500, 302)
        EMU->>RCV: dispatch (retry)
        RCV-->>EMU: non-2xx (forced fail)
    end
    EMU->>RCV: dispatch (final retry)
    RCV-->>EMU: 200 (success)
    T->>RCV: GET /captures?run=PREFIX
    RCV-->>T: recorded per-attempt headers
    Note over T: diff against golden/dispatch.json

Because the receiver is hosted on App Engine, its HTTP endpoint also receives App Engine frontend headers (X-Appengine-Api-Ticket, -User-Ip, …) that real Cloud Tasks never sends to an arbitrary HTTP target. normalizeDispatchHeaders allowlists only the actual dispatch headers plus User-Agent, dropping that noise (and placeholdering the run-scoped queue name, task name and ETA) before diffing.

Record it against real Cloud Tasks (needs the receiver deployed - the App Engine app does dispatch real task traffic, unlike the control-plane batteries):

cd test/conformance/receiver && gcloud app deploy app.yaml --project=$PROJECT

cd .. && go run ./cmd/record \
  -target=real -kind=dispatch -project=$PROJECT -location=us-central1 \
  -receiver-url=https://$PROJECT.appspot.com -out=golden/dispatch.json

TestEmulatorDispatch validates the emulator hermetically: it runs the receiver as a local server, points emulator HTTP tasks straight at it and sets APP_ENGINE_EMULATOR_HOST so App Engine tasks reach it too, then diffs the observed headers against golden/dispatch.json (skipping if it's absent). No GCP is involved in validation - only the golden was recorded from real.

Task-size probe

cmd/probe is a discovery tool, not a golden battery: it answers how real Cloud Tasks measures the task size limit (which fields count, and how) by adaptive search, which a fixed case list can't do - the interesting inputs ("exactly at the limit", "one byte over") depend on the answer.

Per target type (HTTP, App Engine) it binary-searches the largest accepted body on a minimal task, records the serialized proto sizes at that boundary and the exact rejection error, then weighs each other field (headers, URL, task ID, explicit method, dispatch deadline, OIDC token, App Engine routing) by testing whether shrinking the body by the field's exact proto-encoding delta puts the task back on the boundary. A consistent verdict across perturbations means the limit tracks the serialized proto; an inconsistent one triggers a fallback search that measures the field's actual weight.

Control-plane only: the probe queue is paused and every task carries a far-future schedule time, so nothing dispatches. Roughly 40 CreateTask calls per target; the queue (and all its tasks) is deleted on the way out.

gcloud auth application-default login
cd test/conformance
go run ./cmd/probe -project=$PROJECT -location=us-central1 -out=/tmp/sizeprobe.json

Reading the report: maxBody and the *ProtoSizeAtMax numbers identify what quantity the limit is defined over (compare against 102400/1048576 vs 100000/1000000); rejectCode/rejectMessage/rejectDetails are what the emulator's error mapping must reproduce; each perturbation is either consistent with its proto encoding, inconsistent (with its measured actual boundary), or inconclusive because a non-size rejection intervened - the oidc-token case needs iam.serviceAccounts.actAs on the named service account to be conclusive, and App Engine-target creation may require the project to have an App Engine app (it does if the dispatch battery's receiver was deployed).

The 2026-08-23 run's findings are enforced in internal/engine/tasksize.go, whose header documents the measured law in full. To re-validate the emulator end-to-end, re-run the probe against it with the real run's resource names pinned (task size depends on name length) and diff the reports - they match field-for-field except the oidc-token case, where real fails on service-account existence (it validates the account exists at create time; the emulator accepts any email) and the emulator instead measures the weight:

go run ./cmd/emulator -port 8123 -app-engine-region-id uc &   # from repo root
cd test/conformance
go run ./cmd/probe -target=emulator -addr=localhost:8123 \
  -project=cloudtasksemu -prefix=cte-probe-1234567890 -out=/tmp/sizeprobe-emu.json

The durable regressions live as robustly-over task/create/*-too-large cases in the errors battery (golden re-record required when adding them) plus byte-exact boundary unit tests in the engine (the errors battery only captures failures).

Scope

Error states, the happy-path battery and the dispatch-headers battery above, plus the task-size probe (a discovery tool - its findings land as engine validation and new error-battery cases, not as a golden). Other success-response shapes remain out of scope.

Documentation

Overview

Package conformance captures the observable error behaviour of Cloud Tasks (real or emulated) by firing a fixed battery of deliberately-malformed and edge-case RPCs at a target and recording the resulting gRPC status.

The same battery runs against the real API (to produce a committed golden snapshot) and against the emulator (to validate it). Because Cloud Tasks interpolates request values into some error messages, results are normalized into templates with placeholders before comparison - see normalize.go.

Index

Constants

This section is empty.

Variables

View Source
var KnownDivergences = map[string]string{
	"queue/create/invalid-parent": "real resolves any parent string to a project and returns PermissionDenied via IAM; the emulator has no project/IAM concept and returns InvalidArgument. Not reproducible by design.",
}

KnownDivergences are cases where the emulator is currently expected to differ from real Cloud Tasks for reasons beyond error-message mapping - genuine behaviour gaps we have chosen to defer. The validation test reports these as KNOWN rather than failing on them, and flags any that have started matching (so the entry can be removed once the gap is closed).

Keep this list short and each entry justified; it is the explicit ledger of "the emulator is not faithful here, on purpose, for now".

Functions

func LoadDispatch

func LoadDispatch(path string) (map[string]DispatchSnapshot, error)

LoadDispatch reads a dispatch golden keyed by case name.

func LoadErrors

func LoadErrors(path string) (map[string]CaseResult, error)

LoadErrors reads an error-battery golden keyed by case name.

func LoadHappyPath

func LoadHappyPath(path string) (map[string]HappyPathSnapshot, error)

LoadHappyPath reads a happy-path golden keyed by observation name.

func NewEmulatorClient

func NewEmulatorClient(ctx context.Context, addr string) (*cloudtasks.Client, error)

NewEmulatorClient points the same official client at a running emulator over an insecure local channel, with no credentials.

func NewRealClient

func NewRealClient(ctx context.Context) (*cloudtasks.Client, error)

NewRealClient connects to the production Cloud Tasks API using Application Default Credentials (run `gcloud auth application-default login` first). The official client sets the routing headers and per-method deadlines itself, so what we record is exactly what a real caller using the SDK would see.

func Normalize

func Normalize(msg string, p Params) string

Normalize replaces request-specific substrings in a message with stable placeholders, so messages captured with different inputs compare equal. The derived "template" is what we store and diff. Replacements run most-specific first so longer resource paths are matched before their components.

Anything that survives normalization is treated as static text. By running a case with several differing inputs and checking the templates agree (see CaseResult.Stable), we confirm we have correctly identified every interpolated slot - any input value we failed to placeholder would leak through and make the variants disagree.

func SaveDispatch

func SaveDispatch(path string, snaps []DispatchSnapshot) error

SaveDispatch writes dispatch snapshots to path (see saveGolden).

func SaveErrors

func SaveErrors(path string, results []CaseResult) error

SaveErrors writes error-battery results to path (see saveGolden). Per-variant detail is dropped from the committed golden - the aggregate is the contract; variants are kept only in ad-hoc dumps if a caller wants them.

func SaveHappyPath

func SaveHappyPath(path string, snaps []HappyPathSnapshot) error

SaveHappyPath writes happy-path snapshots to path (see saveGolden).

Types

type Captured

type Captured struct {
	Headers map[string]string `json:"headers,omitempty"`
	Body    []byte            `json:"body,omitempty"`
	Err     string            `json:"err,omitempty"`
}

Captured is what one read observed - the task's headers and body - or the error that read returned. Headers and Body are nil when Err is set.

type Case

type Case struct {
	Name     string
	RPC      string
	Category string
	Setup    func(ctx context.Context, c *Client, p Params) error
	Invoke   func(ctx context.Context, c *Client, p Params) error
	Teardown func(ctx context.Context, c *Client, p Params) error
}

Case is one error scenario exercised against a target. Setup establishes any precondition (e.g. create-then-delete to reach "recently deleted"); Invoke performs the RPC whose error we want to capture; Teardown is best-effort cleanup. Setup/Teardown may be nil.

Each case is run with several Params variants so the recorder can distinguish static message text from interpolated request values.

func Cases

func Cases() []Case

Cases is the full error-state battery. Names are stable identifiers used as golden keys, so do not rename casually.

type CaseResult

type CaseResult struct {
	Name     string         `json:"name"`
	RPC      string         `json:"rpc"`
	Category string         `json:"category"`
	Code     string         `json:"code"`
	Template string         `json:"template"`
	Details  []DetailRecord `json:"details,omitempty"`
	Stable   bool           `json:"stable"`
	Variants []Result       `json:"variants,omitempty"`
}

CaseResult aggregates a case's variants into the canonical record that goes into the golden file. Stable is true when every variant agreed on code and template after normalization; when false the variants diverged and the template should not be trusted as-is.

func RunErrors

func RunErrors(ctx context.Context, c *Client, opts RunOptions) []CaseResult

RunErrors executes the error battery against the client and returns one CaseResult per case. It never aborts on an individual RPC failure - failures are the data being collected.

type Client

type Client = cloudtasks.Client

Client is the official Cloud Tasks client; the same type drives both the real API and the emulator.

type DetailRecord

type DetailRecord struct {
	Type     string `json:"type"`
	Template string `json:"template"`
}

DetailRecord is one entry from a gRPC status' details, with its message text normalized the same way as the top-level message.

type Diff

type Diff struct {
	Case  string
	Field string
	Want  string // golden
	Got   string // recorded
}

Diff describes one mismatch between a recorded result and the golden.

func CompareDispatch

func CompareDispatch(golden map[string]DispatchSnapshot, got []DispatchSnapshot) []Diff

CompareDispatch checks recorded dispatch snapshots against a golden, returning a Diff per case whose formatted attempts differ.

func CompareErrors

func CompareErrors(golden map[string]CaseResult, got []CaseResult) []Diff

CompareErrors checks recorded error-battery results against a golden, returning a Diff per mismatched code, template or details.

func CompareHappyPath

func CompareHappyPath(golden map[string]HappyPathSnapshot, got []HappyPathSnapshot) []Diff

CompareHappyPath checks recorded snapshots against a golden, returning a Diff per mismatched read stage (headers or body).

func (Diff) String

func (d Diff) String() string

type DispatchAttempt

type DispatchAttempt struct {
	Attempt int               `json:"attempt"` // X-*-TaskRetryCount (0 on first delivery)
	Status  int               `json:"status"`  // status the receiver returned for this attempt
	Headers map[string]string `json:"headers"` // dispatch headers, filtered + normalized
}

DispatchAttempt is what the receiver observed for one delivery attempt of a dispatched task.

type DispatchSnapshot

type DispatchSnapshot struct {
	Name        string            `json:"name"`        // stable golden key, e.g. "dispatch/http"
	RequestType string            `json:"requestType"` // "http" | "appengine" | "http-timeout"
	Attempts    []DispatchAttempt `json:"attempts"`    // sorted by Attempt ascending
}

DispatchSnapshot is one case's golden entry: every delivery attempt the receiver observed for that case's task, in attempt order. A healthy standard case runs through the whole forced-status sequence before a 200 (six attempts); the timeout case has two (a timed-out attempt, then a 200). Fewer than expected means a retry did not happen (or the task never dispatched) - that gap is itself meaningful and is surfaced by the record command rather than silently swallowed.

func RunDispatch

func RunDispatch(ctx context.Context, c *Client, opts RunOptions, receiverURL string) []DispatchSnapshot

RunDispatch executes the dispatch battery against the client and returns one snapshot per case. receiverURL is the base URL of a running receiver (see test/conformance/receiver) - either a deployed App Engine app (to record the golden from real Cloud Tasks) or a local server (for hermetic emulator validation). Like the other batteries, a failure at any stage is recorded as data (an empty or short Attempts slice) rather than aborting the run.

type HappyPathSnapshot

type HappyPathSnapshot struct {
	Name        string            `json:"name"`
	RequestType string            `json:"requestType"` // http | appengine
	Sent        map[string]string `json:"sent"`
	CreateFull  Captured          `json:"createFull"` // CreateTask response, FULL view
	GetBasic    Captured          `json:"getBasic"`   // GetTask, BASIC view
	GetFull     Captured          `json:"getFull"`    // GetTask, FULL view
}

HappyPathSnapshot is one observation's golden entry: the headers we submitted alongside what Cloud Tasks echoed back at each read stage.

func RunHappyPath

func RunHappyPath(ctx context.Context, c *Client, opts RunOptions) []HappyPathSnapshot

RunHappyPath executes the happy-path battery against the client and returns one snapshot per observation. Like Run, it never aborts on an individual RPC failure - a failure is recorded in the relevant Captured.Err (e.g. a FULL-view read without cloudtasks.tasks.fullView) and is itself data.

type Params

type Params struct {
	Project  string
	Location string
	QueueID  string
	TaskID   string
}

Params are the request-shaping values for one invocation of a case. The runner generates several variants per case with differing QueueID/TaskID so normalize.go can tell static message text from interpolated request values.

func (Params) Parent

func (p Params) Parent() string

Parent is the location resource name (CreateQueue parent, ListQueues parent).

func (Params) QueuePath

func (p Params) QueuePath() string

QueuePath is the fully-qualified queue resource name.

func (Params) TaskPath

func (p Params) TaskPath() string

TaskPath is the fully-qualified task resource name.

type PerturbationReport

type PerturbationReport struct {
	Name string `json:"name"`
	// ProtoDelta is how much the perturbation grows the serialized Task proto.
	ProtoDelta int `json:"protoDelta"`
	// PredictedMaxBody is baseline MaxBody - ProtoDelta: where the boundary
	// lands if the perturbed field counts exactly its proto encoding.
	PredictedMaxBody int    `json:"predictedMaxBody"`
	AtPredicted      string `json:"atPredicted"`   // outcome at PredictedMaxBody
	OverPredicted    string `json:"overPredicted"` // outcome at PredictedMaxBody+1
	// Consistent means accepted at the predicted max and size-rejected one
	// over: the field's weight matches its proto encoding exactly.
	Consistent bool `json:"consistentWithProtoSize"`
	// ActualMaxBody is measured by a full binary search when the prediction
	// failed; -1 when the prediction held (or the search was inconclusive).
	ActualMaxBody int    `json:"actualMaxBody"`
	Note          string `json:"note,omitempty"`
}

PerturbationReport records one field's measured contribution to task size.

type Result

type Result struct {
	Params   Params         `json:"params"`
	SetupErr string         `json:"setupErr,omitempty"`
	Code     string         `json:"code"`
	Message  string         `json:"message"`
	Template string         `json:"template"`
	Details  []DetailRecord `json:"details,omitempty"`
}

Result is the captured outcome of a single (case, variant) invocation.

type RunOptions

type RunOptions struct {
	Project  string
	Location string
	Prefix   string // run-scoped resource-name prefix, keeps re-runs from colliding
	Variants int    // number of differing-input variants per case (>=2 to detect templates)
}

RunOptions configure a recording run.

type SizeProbeOptions

type SizeProbeOptions struct {
	Project  string
	Location string
	Prefix   string    // run-scoped resource-name prefix, keeps re-runs from colliding
	Targets  []string  // subset of {"http", "appengine"}; empty probes both
	Log      io.Writer // per-attempt progress log; nil discards
}

SizeProbeOptions configure a size-probe run.

type TargetSizeReport

type TargetSizeReport struct {
	Target string `json:"target"`
	Err    string `json:"err,omitempty"` // set when the probe aborted for this target

	// MaxBody is the largest accepted body length on the baseline task.
	MaxBody int `json:"maxBody"`

	// Serialized proto sizes of the baseline task at the MaxBody boundary,
	// for matching the measured limit against candidate formulas.
	TaskProtoSizeAtMax    int `json:"taskProtoSizeAtMax"`
	RequestProtoSizeAtMax int `json:"requestProtoSizeAtMax"`
	MessageProtoSizeAtMax int `json:"messageProtoSizeAtMax"` // the HttpRequest / AppEngineHttpRequest submessage

	// The first over-limit rejection observed, verbatim - this is what the
	// emulator's error mapping should reproduce.
	RejectCode    string         `json:"rejectCode"`
	RejectMessage string         `json:"rejectMessage"`
	RejectDetails []DetailRecord `json:"rejectDetails,omitempty"`
	// OtherRejections lists any rejection whose digit-stripped template
	// differs from the first - non-empty means not every rejection during the
	// search was the same (size) error, so inspect before trusting MaxBody.
	OtherRejections []string `json:"otherRejections,omitempty"`

	Perturbations []PerturbationReport `json:"perturbations,omitempty"`

	Calls int `json:"calls"` // CreateTask calls spent on this target
}

TargetSizeReport is what the probe learned about one target type.

func RunSizeProbe

func RunSizeProbe(ctx context.Context, c *Client, opts SizeProbeOptions) []TargetSizeReport

RunSizeProbe probes each requested target type. Per-target failures land in the report's Err field rather than aborting the run, so one target's environment problem (e.g. no App Engine app in the project) doesn't cost the other's results; only context cancellation stops the whole run.

Directories

Path Synopsis
cmd
probe command
Command probe adaptively discovers how Cloud Tasks measures the task size limit: the exact boundary per target type, which fields count toward it, and the exact rejection error.
Command probe adaptively discovers how Cloud Tasks measures the task size limit: the exact boundary per target type, which fields count toward it, and the exact rejection error.
record command
Command record fires the conformance battery at a target (real Cloud Tasks or a running emulator) and writes the captured results as JSON.
Command record fires the conformance battery at a target (real Cloud Tasks or a running emulator) and writes the captured results as JSON.
receiver module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL