scold

package module
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2022 License: BSD-3-Clause Imports: 22 Imported by: 0

README

scold

Coverage Status GoReport PkgGoDev Actions Status Release

A tool to speed up the testing of competitive programming code on multiple inputs. I.e., the purpose is to minimize the number of keypresses between finishing writing code and submitting it, knowing that the code passes on the provided test inputs.

Install

Windows

On Windows go to the releases page on the right and download the scold executable for your architecture. Rename it to scold.exe. Additionally, you can create a folder, add it to the PATH and put scold.exe in there to access scold from the console.

You can do all of this automatically by using scoop. scoop is a minimalistic install helper for Windows that allows you to install, update, and manage various command-line utilities and applications (python, go, node.js) from within the console in just one line. Get it from scoop.sh. If you're a fan of Linux, you will be pleased that you can install all of the Linux tools via scoop and enjoy your Linux habits on Windows. Also, if you need some app, just scoop search to see if you can have it without a hassle.

When you installed scoop, run

> scoop update

And then add the bucket with the install script and install scold.

> scoop bucket add kuredoro https://github.com/kuredoro/scoop-bucket
> scoop install scold

Linux

You can use AUR on ArchLinux to install scold. Via yay AUR helper it would be:

$ yay -S scold

Don't forget to star packages on AUR if you liked them (─‿‿─).

For other distros, you'll have to build it from the source, but don't worry, it's just one line. See Building.

User guide

Command-line interface

scold [options] EXECUTABLE [ARGS...]

scold requires an executable to run. Any arguments written after the executable are forwarded to it. This way, one can call scold node index to test a Node.js code. The options related to the scold are, therefore, specified before the executable.

Possible arguments:

  • -i, --inputs -- specifies the path to the test suite. Default: inputs.txt.
  • -j, --jobs -- specifies the number of executables to run concurrently. Default: CPU count.
  • --no-colors -- disables colored output. Useful for environments that cannot render color, like Sublime Text console.
  • --no-progress -- disables progress bar. Since the progress bar requires some specific features of a console emulator, it might not work everywhere.

inputs.txt format

The format is simple:

===     (1)
line 1  ┐
line 2  │
---     ├─ Test 1
line 1  │
line 2  ┘
===     (1)
===
line 1  ┐
line 2  │
---     ├─ Test 2
line 1  │
line 2  ┘
===     (1)

In other words, your inputs are separated from outputs with ---, and test cases are separated with ===. The empty test cases (1) are ignored and allowed. All of the lines in the input and output sections will be newline terminated when parsed by scold.

The first test can specify a set of test suite options and follows a different format

tl = 10s
prec = 8
===
line 1
line 2
---
line 1
line 2

See Test suite configuration.

How are outputs compared?

TL;DR: The comparison routine is more or less equivalent to a program that reads the program's output from the stdin and compares it against correct values.

The key concepts in scold are the lexeme and the lexeme type. Lexeme is a string consisting of printable characters or a single newline. The program's output and the test's answer are parsed and turned into a sequence of lexemes while discarding all whitespaces, tabs, etc. between them.

Given this sequence, the lexeme type is deduced for each lexeme. The lexeme type specifies what type of data the lexeme holds. Currently, there are string, integer and floating-point number types. There is a specialization, or an "is-a", relation between the types. For example, every integer is a string, but not every string is an integer, hence integer specializes string type. In fact, specialization relation is the weak order relation: >=, and it follows that: string >= floating-point number >= integer.

For each lexeme at the same position in both sequences (program's output and the answer) their common type is deduced by taking the least specialized type among the two lexemes. Then a comparison routine is invoked that performs highlighting of the mismatched parts depending on the deduced common type. For example, if a discrepancy is found in an integer, the whole integer should be highlighted, instead of individual characters, otherwise, it would be annoying and not logically correct.

Additional measures are taken to treat excessive newlines rationally. If a misplaced newline is encountered (meaning that the other lexeme is not a newline), the lexemes after this newline are skipped until a non-newline lexeme is encountered. This ensures that in case of an excessive newline, the comparison highlighting stays consistent and valid.

Verdicts

OK: Test pass

Example:

--- OK: Test 1 (0.123s)

OK verdict reflects the answer and the program's output being conceptually equal and that the program finished within the specified time frame.

Even if the program has some stderr output, it won't be displayed.

WA: Wrong answer

Example:

--- WA:	Test 1 (0.523s)
Input:
5
5 4 3 2 1

Answer:
5
1 2 3 4 5\n

Output:
5
1 2 4 3 5

Stderr:
Some debug information...

WA verdict signalizes that the program's output somehow differs from the correct answer so much that the output cannot be any longer considered correct.

The discrepancies are highlighted by default and the behavior of the highlight is different depending on the nature of data the inconsistency is found in. If the lexemes that are different are

  • floating-point numbers -- the sign, the whole part or/and the fractional part up until the specified precision is highlighted.
  • integer numbers -- the sign or/and all of the digits are highlighted.
  • strings -- the individual characters are highlighted.

Moreover, if a number is too long, it's treated as a string.

The newlines are not visible, but if one is missing or misplaced, it will be highlighted and rendered as text, in this example, as \n. Since a misplaced newline can skew all of the lexemes that follow it, the extra newlines are ignored as if they have never been in the input while comparing further lexemes.

Next, to aid debugging, the stderr is also being printed (if it has anything). This way, a printf-style debugging can not interfere with the output that needs to be compared. Further adjustments to the user's code can create a special logger that will output to stderr and that can be easily turned off before sending the code to the online judge.

Some OJ add ONLINE_JUDGE preprocessor macro when compiling C/C++ code, like Codeforces or UVa. You can use #ifdef ONLINE_JUDGE to conditionally turn off debugging when sending to the OJ. Or do this in reverse: define a macro that will be present when compiling locally, like g++ main.cpp -DLOCAL_BUILD and use it instead. More ideas and for more languages can be found in this Codeforces thread.

RE: Runtime error

Example:

--- RE:	Test 1 (0.005s)
Input:
1

Answer:
1\n

Exit code: 255

Output:
Something from stdout

Stderr:
Something from stderr

Runtime errors are detected by looking at the exit code. If it has a non-zero value -- it's a RE. When this happens, additional information is always printed: the exit code and the stderr.

TL: Time limit exceeded

Example:

--- TL:	Test 1 (6.000s)
Input:
abcde

Answer:
edcba\n

When the program exceeds the default time limit or the one specified by the user (see Specifying time limit), the program is terminated and the test is failed. Nor the output, nor the stderr are shown.

IE: Internal error

Example (on Linux):

--- IE:	Test 160 (0.169s)
Input:
55

Answer:
55\n

Error:
executable: fork/exec /home/kuredoro/contest_code/a.out: too many open files

The internal error is a failed test because scold could not perform what it was designed to do. The situations when IE pops out are extremely rare but sometimes can occur. In the example above, the problem is that the executable a.out was opened too many times simultaneously exceeding the limit Linux allows an executable to be opened at the same time (on the machine in question). The IE can also appear when scold panics itself, in which case it might be a potential bug. As always, read what the error says and, if anything, ask for help or file a bug on the issue tracker.

Test suite configuration

A set of key-value pairs can be specified at the very top of inputs.txt. For example:

tl = 10s
prec = 8
===
input 1
---
output 1
===
input 2
---
output 2

A key-value pair is a line with an equality sign. The key and the value are located to the left and to the right of the sign, respectively. They both are space-trimmed. So, " two words = are parsed" is parsed as: key="two words" and value="are parsed".

Specifying time limit

Syntax:

tl = <digits> [ '.' <digits> ] <unit>
unit ::=  "ns" | "us" | "µs" | "ms" | "s" | "m" | "h"

Examples:

tl = 1ms
tl = 6.66s
tl = 0.01m
tl = 0s

The tl option specifies the time limit for the test suite, overriding the default value. The value for the time limit should contain a unit suffix and may contain a fractional part. "us" and "µs" both correspond to microseconds. If tl is specified to be 0, then the time limit is considered to be infinite.

Specifying floating point precision

Syntax:

prec = <digits>

Examples:

prec = 0
prec = 12

The prec option specifies how many digits after the decimal point should be considered when comparing floating-point lexemes. The value of 0 tells scold to ignore the fractional part.

Building

To build scold you'll need an installation of go. Installing it should be as simple as installing base-devel package (─‿‿─).

E.g. on Arch:

$ sudo pacman -S go

Then clone the repository, and invoke go build.

$ git clone https://github.com/kuredoro/scold.git
$ cd scold
$ go build ./cmd/scold

Finally, move the executable to a folder accessible from PATH, like

$ sudo mv scold /usr/bin

You're ready to go!

Documentation

Index

Constants

View Source
const (
	STRXM lexemeType = iota
	FLOATXM
	INTXM
	FINALXM
)

Available lexeme types. The following invariant holds: each type is a specialization of all types whose numerical value is less than that of self. For example, 42 is a float and is an int, but 42.2 is a float but not an int. Hence, int is a specialization of float. A type T is a specialization of a type U if any value of type T is of type U also.

The consequence is that between any two types T and U from the list there's always a specialization relationship, but in gerenal, this is not the case. For example: imagine a lexeme type 'hash' that classifies strings of form 2400f9b. The float is not a specialization of hash, because 42.2 is not a hash, and likewise the hash is not a specialization of float, because 2400f9b is not a float.

View Source
const (
	IOSeparatorMissing = StringError("IO separator missing")
	KeyMissing         = StringError("key cannot be empty")
)

A set of errors that may be produced during scanning of the inputs file.

View Source
const (
	IODelim   = "---"
	TestDelim = "==="
)

The set of delimeters used when partitioning inputs file.

View Source
const (
	// ErrNotAStructLike is issued when a destination type doesn't behave like
	// a struct. struct and *struct types have the same syntax for manipulating
	// them, so they are considered struct-like.
	ErrNotAStructLike = StringError("not a struct-like")

	// ErrUnknownField is issued when the deserialization destination cannot be
	// found in a struct-like via reflection.
	ErrUnknownField = StringWarning("unknown field")

	// ErrNegativePositiveDuration is issued when PositiveDuration is attempted
	// to be initialized with negative value.
	ErrNegativePositiveDuration = StringError("PositiveDuration accepts only positive durations")

	// ErrDurationWithoutSuffix is issued when PositiveDuration is unmarshalled
	// with a value that doesn't have a unit suffix.
	ErrDurationWithoutSuffix = StringWarning("durations without a unit suffix like \"s\" or \"us\" are deprecated and discouraged. Please, specify the unit explicitly, like \"10.0s\" for 10 seconds")

	// ErrDurationBadSyntax is issued when PositiveDuration is unmarshalled
	// with a value that cannot be interpreted as a duration.
	ErrDurationBadSyntax = StringError("bad syntax. Correct values could be \"1s\" or \"12.3ms\"")
)
View Source
const AltLineFeed = "\\n"

AltLineFeed is the representation of LF in textual form, that replaces LF when it's colorized.

Variables

Au is used to colorize output of several functions. A user of the library can change its value to disable colored output. Refer to the aurora readme for that.

View Source
var MaskGenerators = []func(*Lexer, string, string) []bool{
	(*Lexer).GenMaskForString,
	(*Lexer).GenMaskForFloat,
	(*Lexer).GenMaskForInt,
}

MaskGenerators lists all of the mask generating functions (MGF). MGFs are defined only for arguments of the same type. I.e., there's no MGF for float and int, only for float/float, and int/int. If differnt types must be assessed, the MGF of their common type_ must be called. The common type between two types T and U exists if specialization relationship between them exists and is the least specialized type. The index of MGF in this array corresponds to the numerical value of the type of which MGF's arguments are.

View Source
var TypeCheckers = []func(string) bool{
	IsFloatLexeme,
	IsIntLexeme,
}

TypeCheckers defines a list type checking functions (TCF). The type checker for string is omitted because it always returns true. Hence, the index of TCF corresponds to a type of numerical value `index+1`.

View Source
var ValidIntMaxLen = 10

ValidIntMaxLen is maximum number of digits a lexeme may have to be considered an int

Functions

func AssertCallCount

func AssertCallCount(t *testing.T, funcName string, got, want int)

AssertCallCount checks that the received and expected number of calls are equal.

func AssertDefaultConfig

func AssertDefaultConfig(t *testing.T, got InputsConfig)

AssertDefaultConfig checks that the received key-value set is empty. If it's not, the test is failed and the its contents are printed.

func AssertDiffFailure

func AssertDiffFailure(t *testing.T, ok bool)

AssertDiffFailure chacks if lexeme comparison returned ok = false.

func AssertDiffSuccess

func AssertDiffSuccess(t *testing.T, ok bool)

AssertDiffSuccess chacks if lexeme comparison returned ok = true.

func AssertEnrichedLexSequence

func AssertEnrichedLexSequence(t *testing.T, got, want []RichText)

AssertEnrichedLexSequence checks that both lexeme sequences render to the same string.

func AssertErrors

func AssertErrors(t *testing.T, got, want []error)

AssertErrors compared received array of errors with the expected one.

func AssertLexemes

func AssertLexemes(t *testing.T, got, want []string)

AssertLexemes compares if the two LexSequences are equal.

func AssertListenerNotified

func AssertListenerNotified(t *testing.T, listener *SpyPrinter, wantTests []Test)

func AssertNoErrors

func AssertNoErrors(t *testing.T, errs []error)

AssertNoErrors will check if the array of errors is empty. If it's not empty, the test will be failed and the errors will be reported.

func AssertResultIDInvariant

func AssertResultIDInvariant(t *testing.T, b *TestingBatch)

AssertResultIDInvariant checks that each key in TestingBatch.Results equals to the its value`s ID field.

func AssertRichText

func AssertRichText(t *testing.T, got, want RichText)

AssertRichText checks that the contents and the bitmasks of both RichTexts are equal.

func AssertRichTextMask

func AssertRichTextMask(t *testing.T, got, want []bool)

AssertRichTextMask checks that both bitmasks to be used in RichText are equal.

func AssertTest

func AssertTest(t *testing.T, got Test, want Test)

AssertTest compare the inputs and outputs with respective expected ones for equivalence.

func AssertTests

func AssertTests(t *testing.T, got []Test, want []Test)

AssertTests will compare received array of tests with the expected one.

func AssertText

func AssertText(t *testing.T, got, want string)

AssertText checks two strings for equality, but escapes all newlines.

func AssertThreadCount

func AssertThreadCount(t *testing.T, pool *SpyThreadPool, want int)

AssertThreadCount thread-safely checks that the pool has the specified number of threads created during its lifetime.

func AssertTimes

func AssertTimes(t *testing.T, got map[int]*TestResult, want map[int]time.Duration)

AssertTimes check whether the received and expected timestampts for the test cases both exist and are equal.

func AssertVerdicts

func AssertVerdicts(t *testing.T, got map[int]*TestResult, want map[int]Verdict)

AssertVerdicts checks that received and expected verdict maps contain the same keys, and then checks that the values for these keys equal.

func DumpLexemes

func DumpLexemes(xms []RichText, color aurora.Color) string

DumpLexemes is used to transform array of possibly colorized lexemes into a human readable format. The lexemes are separated by spaces. There are no trailing spaces. Colorized newlines are replaced by printable AltLineFeed string + a newline.

func IsFloatLexeme

func IsFloatLexeme(xm string) bool

IsFloatLexeme returns true if the string represents a floating-point value. Although, there can be no floating-point inside of it. A floating-point value is of form int_part['.' ('0'-'9')*]

func IsIntLexeme

func IsIntLexeme(xm string) bool

IsIntLexeme returns true if the string represents a signed integer. Additionally, it should contain not more than VALID_INT_MAX_LEN digits.

func ScanConfig

func ScanConfig(text string) (config map[string]string, key2line map[string]NumberedLine, errs []error)

ScanConfig tries to parse a stream of key-value pairs. Key-value pair is defined as `<string> "=" <string>`. Both strings are space trimmed. The key must be non-empty. Otherwise, a LineRangeError is issued. Duplicate keys are allowed, the later occurrence is preferred.

The function returns two maps: the first is a key-value map as defined in the supplied text, and the second maps keys to the relevant lines inside the config. The first one will contain only correctly defined keys. The second one is mainly used to correlate errors from StringMapUnmarshal to the inputs.txt lines and produce error messages.

func ScanKeyValuePair

func ScanKeyValuePair(line string) (string, string, error)

ScanKeyValuePair parses the key-value pair of form 'key=value'. Strings without assignment are treated as keys with empty value. Strings with assignment but with empty key are erroneous. The space around key and value respectively is trimmed.

func ScanLexemes

func ScanLexemes(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanLexemes is a split function for bufio.Scanner. It is same as bufio.ScanWords, except that it treats \n character in a special way. \n cannot be in any lexeme, except for "\n" itself. Hence, several \n\n are parsed as separate lexemes ("\n", "\n"). It will never return an empty lexeme. The definition of other spaces is set by unicode.IsSpace.

func SplitByInlinedPrefixN

func SplitByInlinedPrefixN(text, delim string, n int) (parts []string)

SplitByInlinedPrefixN works in the same way as strings.SplitN. However, it does one additional thing. It matches the *prefixes of the lines* for equality with the delimeter. Upon match the entire line is discarded.

If text doesn't contain the delimeter, only one part is returned. User can specify the number of parts they want at most via the third argument.

func StringMapUnmarshal

func StringMapUnmarshal(kvm map[string]string, data interface{}, transformers ...func(string) string) error

StringMapUnmarshal accepts a string map and for each key-value pair tries to find an identically named field in the provided object, parse the string value according to the field's type and assign the parsed value to it.

The field's type should be: int (any flavor), uint (any flavor), string, bool, any struct or a pointer to a struct. The structs should implement encoding.TextUnmarshaler standard interface to be parsed by this function, otherwise NotUnmarshalableTypeError is issued.

If the destination object is not struct or a pointer to a struct, ErrNotAStructLike is issued.

If a map's key cannot be mapped to a field within the destination object, FieldError wrapping ErrUnknownField is issued.

If a map's value cannot be parsed to the destination type, a FieldError wrapping a NotValueOfTypeError is issued.

StringMapUnmarshal makes sure that if any error occurs during unmarshaling of a field, the field's previous value is retained.

This function will accumulate all produced errors and return an instance of *multierror.Error type.

Since a key must match the field's name perfectly, and this function operates only on exported fields, this would mean that only capitalized keys would be accepted. This may not be desired. An array of transformers can be supplied to change the field matching behavior. Each map's key will be fed to one transformer at a time in the order they were passed to this function, until a match is found. The transformer's job, then, is to convert an arbitrary string to a possible exported field's name in the destination object. If a transformer succeeds, the successive transformers are not applied. If the field still could not be found, a FieldError wrapping ErrUnknownField is issued.

Types

type ConfigurableStopwatcher

type ConfigurableStopwatcher struct {
	TL    time.Duration
	Clock clockwork.Clock
}

ConfigurableStopwatcher implements Stopwatcher and allows its user to fully customize it with no make function required.

func (*ConfigurableStopwatcher) Elapsed

func (s *ConfigurableStopwatcher) Elapsed(since time.Time) time.Duration

Elapsed will return the duration since the `since` time, or zero if `since` is in the future.

func (*ConfigurableStopwatcher) Now

Now will return time point since internal's clock epoch.

func (*ConfigurableStopwatcher) TimeLimit

func (s *ConfigurableStopwatcher) TimeLimit(since time.Time) <-chan time.Time

TimeLimit for a given `since` time point returns a channel that will return at `since` + TL time point the very same time point. I.e., it works just just like time.After, but you can specify the *time* after which you want to be notified.

type ExecutionResult

type ExecutionResult struct {
	ExitCode int
	Stdout   string
	Stderr   string
}

ExecutionResult contains the text printed to stdout and stderr by the process and the exit code returned upon termination.

type FieldError

type FieldError struct {
	FieldName string
	Err       error
}

FieldError is a generic error that can be produced while unmarshaling string maps. It enriches error Err with the name of the field relevant to it.

func (*FieldError) Error

func (e *FieldError) Error() string

Error renders the underlying error preceeded with the field's name.

func (*FieldError) Unwrap

func (e *FieldError) Unwrap() error

Unwrap makes FieldError usable with built-in errors package.

type Inputs

type Inputs struct {
	Tests  []Test
	Config InputsConfig
}

Inputs contains all information located in the inputs file: tests and a valid configuration that were provided. Inputs is supposed to be copied around.

func ScanInputs

func ScanInputs(text string) (inputs Inputs, errs []error)

ScanInputs is the main routine for parsing inputs file. It splits the input by test case separator, and tries to parse each individual test case one by one. At the very beginning of the input file a configuration map can be specified (refer to ScanConfig for syntax), granted that it is not part of a meaninful test case. The empty tests are skipped (those that composed of non-printable characters and that don't contain IO separator). If a test case could not be parsed, parsing continues to the next test case, but the errors are accumulated and returned together.

type InputsConfig

type InputsConfig struct {
	Prec uint8
	Tl   PositiveDuration
}

InputsConfig defines a schema for available configuration options that can be listed inside a config.

var DefaultInputsConfig InputsConfig

DefaultInputsConfig is used to define default values for the InputsConfig inside Inputs. It is a starting ground. It may be altered further by customization points inside the inputs.txt file.

type Lexer

type Lexer struct {
	Precision uint
}

Lexer is a set of settings that control lexeme scanning and comparison. And the methods for scanning and comparison are conviniently methods of Lexer.

func (*Lexer) Compare

func (l *Lexer) Compare(target, source []string) (rts []RichText, ok bool)

Compare compares target against source and generates colored target's lexems highlighting mismatches between them. Additionally, actual comparison takes place between two non-LF lexems, and the spurious LFs are marked red and skipped. The function is intended to be called twice for the two permutations of the arguments to get error highlighting for both strings.

func (*Lexer) GenMaskForFloat

func (l *Lexer) GenMaskForFloat(target, source string) (mask []bool)

GenMaskForFloat uses the same logic as GenMaskForInt to highlight the whole part. If at least one digit in the fractional part (part after the dot) is different and its index (zero-based) is less than lexer's precision, this digit is highlighted.

func (*Lexer) GenMaskForInt

func (l *Lexer) GenMaskForInt(target, source string) (mask []bool)

GenMaskForInt will highlight the whole number if at least one digit is different. Independently, the sign will be highlighted if it's different also.

func (*Lexer) GenMaskForString

func (l *Lexer) GenMaskForString(target, source string) (mask []bool)

GenMaskForString will highlight mismatching characters.

func (*Lexer) GenerateMask

func (l *Lexer) GenerateMask(target, source string) []bool

GenerateMask is a wrapper function that finds the common type of the two lexems and generates a color mask for the target based on source.

func (*Lexer) Scan

func (l *Lexer) Scan(text string) (xms []string)

Scan will break the text into lexemes and return them. A lexeme is either a string consisting of non-unicode.IsSpace characters, or a single newline character. If no lexemes found, nil is returned.

type LineRangeError

type LineRangeError struct {
	Begin int
	Lines []string
	Err   error
}

LineRangeError is used to amend information about the location of the error within the source code. The source code lines in question are stored in Lines (for printing purposes).

func (*LineRangeError) CodeSnippet

func (e *LineRangeError) CodeSnippet() string

CodeSnippet is responsible for producing pretty printed source code lines with corresponding line numbers to the left of them.

func (*LineRangeError) Error

func (e *LineRangeError) Error() string

Error renders contents of Err.Error() preceeded by a line number (or a line range), followed by the line numbered code snippet (if provided) on a new line. The error string is terminated with '\n', unless Lines is not nil. It uses Reason and CodeSnippet under the hood.

func (*LineRangeError) Reason

func (e *LineRangeError) Reason() string

Reason is a stripped down version of Error in sense that it returns only the underlying error's message and a line information, with no '\n' at the end.

func (*LineRangeError) Unwrap

func (e *LineRangeError) Unwrap() error

Unwrap makes LineRangeError usable with built-in errors package.

type NotTextUnmarshalableTypeError

type NotTextUnmarshalableTypeError struct {
	Field    string
	Type     reflect.Kind
	TypeName string
}

NotTextUnmarshalableTypeError is a panic error. Value of this type is passed to panic(), mainly during unmarshaling of string maps.

func (*NotTextUnmarshalableTypeError) Equal

Equal is used to define equality on NotTextUnmarshalableTypeError pointers. Used by go-testdeep.

func (*NotTextUnmarshalableTypeError) Error

Error renders a helpful message addressed to the developer, describing possible reasons as to why type could not be unmarshaled.

type NotValueOfTypeError

type NotValueOfTypeError struct {
	Type  string
	Value string
	Err   error
}

NotValueOfTypeError is a generic basic error type that describes a value and a type that don't match. This error is produced mainly during unmarshaling string maps.

func (*NotValueOfTypeError) Equal

func (e *NotValueOfTypeError) Equal(other *NotValueOfTypeError) bool

Equal is used to define equality on the pointers of NotValueOfTypeError. Used by go-testdeep.

func (*NotValueOfTypeError) Error

func (e *NotValueOfTypeError) Error() string

Error prints a human-readable message describing what value doesn't match what type.

func (*NotValueOfTypeError) Unwrap added in v0.7.0

func (e *NotValueOfTypeError) Unwrap() error

Unwrap returns the reason for the error, if available.

type NumberedLine

type NumberedLine struct {
	Num  int
	Line string
}

NumberedLine is used to assign line number information to a string. It is used to map string map keys produced during config parsing to the relevant lines for error reporting.

type PositiveDuration

type PositiveDuration struct{ time.Duration }

PositiveDuration is a wrapper around time.Duration that allows StringMapsUnmarshal to parse it from a string and that forbids negative durations. Implements encoding.TextUnmarshaler.

func NewPositiveDuration

func NewPositiveDuration(dur time.Duration) PositiveDuration

NewPositiveDuration returns a PositiveDuration with the specified value. Panics if value is negative.

func (*PositiveDuration) UnmarshalText

func (d *PositiveDuration) UnmarshalText(b []byte) error

UnmarshalText will delegate parsing to built-in time.ParseDuration and, hence, accept the same format as time.ParseDuration. It will also reject negative durations.

type Processer

type Processer interface {
	Run(context.Context, io.Reader) (ExecutionResult, error)
}

Processer interface abstracts away the concept of the executable under testing.

type ProcesserFunc

type ProcesserFunc func(ctx context.Context, r io.Reader) (ExecutionResult, error)

ProcesserFunc represents an implementation of Processer that instead of a real OS-level process executes Go code.

func (ProcesserFunc) Run

Run will call the underlying Go function to compute the result.

type RichText

type RichText struct {
	Str  string
	Mask []bool
}

RichText represents a text data with additional color metadata in a form of a bitmask. The characters may be either colored or uncolored. The _color_ might represent a literal color or a formatting style like bold or italics.

func (RichText) Colorful

func (rt RichText) Colorful() bool

Colorful returns whether at least one character in Str has color.

func (RichText) Colorize

func (rt RichText) Colorize(color aurora.Color) string

Colorize returns Str with ASCII escape codes actually embedded inside it to enable colors. The resulting string then can be printed on the screen and it'll be colorful, for example.

type Runnable

type Runnable interface {
	Run()
}

Runnable represents a callable object. It allows representing the closure of a function as well-defined struct fields with the function in question having no closure (the Run() method).

type RunnableFunc

type RunnableFunc func()

RunnableFunc adapts basic golang functions for the Runnable interface.

func (RunnableFunc) Run

func (f RunnableFunc) Run()

Run calls the contained function.

type SpyPrinter

type SpyPrinter struct {
	StartedIDs    []int
	FinishedIDs   []int
	FinishedTests []*Test
	Finished      bool
}

SpyPrinter implements TestingEventListener and is only concerned with storing the information received from the callbacks, so that its correctness can be assessed later.

func (*SpyPrinter) SuiteFinished

func (l *SpyPrinter) SuiteFinished(*TestingBatch)

SutieFinished turn Finished true.

func (*SpyPrinter) TestFinished

func (l *SpyPrinter) TestFinished(test *Test, result *TestResult)

TestFinished amends the test and the ID of the test to FinishedTests and FinishedIDs respectively.

func (*SpyPrinter) TestStarted

func (l *SpyPrinter) TestStarted(id int)

TestStarted amends the ID of the test to StartedIDs

type SpyProcesser

type SpyProcesser struct {
	Proc Processer
	// contains filtered or unexported fields
}

SpyProcesser is a test double that proxies another processer. It additionally stores the number of calls made to the Run function.

func (*SpyProcesser) CallCount

func (p *SpyProcesser) CallCount() int

CallCount will return the number of times Run was called. Can be called concurrently.

func (*SpyProcesser) Run

Run will execute the Run function of the inner processer, but will also increase the call count by one.

type SpyThreadPool

type SpyThreadPool struct {
	DirtyThreads map[int]struct{}
	// contains filtered or unexported fields
}

SpyThreadPool implements the same functionality as ThreadPool, but also tracks the goids of the threads involved in task execution. This is used to assert that an exact number of threads was invoked.

func NewSpyThreadPool

func NewSpyThreadPool(threadCount int) *SpyThreadPool

NewSpyThreadPool creates a thread pool with the specified number of preallocated threads in the pool.

func (*SpyThreadPool) Execute

func (p *SpyThreadPool) Execute(task Runnable) error

Execute tries to find a worker to assign the task to. If it doesn't find one, it may or may NOT give an error. When error is returned is unspecified, but it shouldn't happen within general usage.

func (*SpyThreadPool) WorkerCount

func (p *SpyThreadPool) WorkerCount() int

WorkerCount returns the number of currently allocated threads. These may or may not have any tasks assigned to them.

type Stopwatcher

type Stopwatcher interface {
	Now() time.Time
	Elapsed(since time.Time) time.Duration
	TimeLimit(since time.Time) <-chan time.Time
}

Stopwatcher abstracts away the concept of the stopwatch. At any time, one can look up the elapsed time. Additionally, one can be notified when the time is up.

type StringError

type StringError string

StringError is an error type whose values can be constant and compared against deterministically with == operator. An error type that solves the problems of sentinel errors.

const TLError StringError = "Time limit exceeded"

TLError is an error that can occur during Processer execution that indicates that it was prematurely killed by TestingBatch, because it exceeded the time limit.

func (StringError) Error

func (e StringError) Error() string

Error makes StringError satisfy error interface.

type StringWarning added in v0.7.0

type StringWarning string

StringWarning is same as StringError except it allows users to differentiate warnings using errors.As

func (StringWarning) Error added in v0.7.0

func (w StringWarning) Error() string

Error prints the warning message.

type StubTestingEventListener

type StubTestingEventListener struct{}

StubTestingEventListener implements TestingEventsListener, but does nothing. It is used when constructing TestingBatch.

func (*StubTestingEventListener) SuiteFinished

func (*StubTestingEventListener) SuiteFinished(*TestingBatch)

SuiteFinished is a stub that does nothing.

func (*StubTestingEventListener) TestFinished

func (*StubTestingEventListener) TestFinished(*Test, *TestResult)

TestFinished is a stub that does nothing.

func (*StubTestingEventListener) TestStarted

func (*StubTestingEventListener) TestStarted(int)

TestStarted is a stub that does nothing.

type Test

type Test struct {
	Input  string
	Output string
}

Test represents a single test case: an input and the expected output.

func ScanTest

func ScanTest(testStr string) (Test, []error)

ScanTest parses a single test case: input and output, separated with the Input/Output separator. If separator is absent, it returns an error.

type TestError

type TestError struct {
	TestNum int
	Err     error
}

TestError is a scold centric error type that maps a test's index inside inputs.txt to a particular error Err.

func (*TestError) Error

func (e *TestError) Error() string

Error renders the underlying error preceeded with corresponding test number.

func (*TestError) Unwrap

func (e *TestError) Unwrap() error

Unwrap makes TestError usable with built-in errors package.

type TestExecutionResult

type TestExecutionResult struct {
	ID  int
	Err error
	Out ExecutionResult
}

TestExecutionResult carries an output of the process together with the corresponding test ID and a TestingBatch related error if any (panics, etc.).

type TestFinishedCallback

type TestFinishedCallback func(*Test, *TestResult)

TestFinishedCallback allows to use a simple function in places where TestingEventLister is needed, to handle the TestFinished event.

func (TestFinishedCallback) SuiteFinished

func (cb TestFinishedCallback) SuiteFinished(*TestingBatch)

SuiteFinished is a stub that does nothing

func (TestFinishedCallback) TestFinished

func (cb TestFinishedCallback) TestFinished(test *Test, result *TestResult)

TestFinished will call the underlying function with its arguments.

func (TestFinishedCallback) TestStarted

func (cb TestFinishedCallback) TestStarted(int)

TestStarted is a stub that does nothing.

type TestResult

type TestResult struct {
	RichOut    []RichText
	RichAnswer []RichText
	Verdict    Verdict
	Time       time.Duration

	TestExecutionResult
}

TestResult encapsulates all the information TestingBatch produced for a particular test.

type TestingBatch

type TestingBatch struct {
	Results map[int]*TestResult
	Lx      *Lexer

	Proc Processer

	ThreadPool WorkerPool

	Swatch Stopwatcher

	Listener TestingEventListener
	// contains filtered or unexported fields
}

TestingBatch is responsible for running tests and evaluating the verdicts for tests. For each test case, the verdict and execution time are stored. It utilizes an instance of Processer to run tests, and an instance of Stopwatcher to track time limit. Optionally, user can set ResultPrinter to a custom function to output useful statistics about test case's result.

func NewTestingBatch

func NewTestingBatch(inputs Inputs, proc Processer, swatch Stopwatcher, pool WorkerPool) *TestingBatch

NewTestingBatch will initialize channels and maps inside TestingBatch and will assign respective dependency injections.

func (*TestingBatch) Run

func (b *TestingBatch) Run()

Run will lauch test cases in parallel and then will wait for each test to finish or for the time to timelimit. When a test is finished the verdict and the time it took to execute are remembered. Additionally, ResultPrinter is called on the test case's statistics. When a time limit is reached, each not-yet-judged test is assigned TL verdict and the ResultPrinter is also called on each test.

type TestingEventListener

type TestingEventListener interface {
	// TestStarted will be called upon test case lounch.
	TestStarted(id int)

	// TestFinished is called when a test case finishes execution. Usually,
	// one would like to print the test case result information.
	//
	// It accepts a pointer to the contents of the Test and a pointer to the
	// test case result that contains id of the test, its verdict, time, and
	// other useful information related to the executable's execution.
	TestFinished(*Test, *TestResult)

	// SuiteFinished is called when all test cases have finished.
	// Accessing testingBatch at this point is safe, and no events will
	// be sent to TestingEventListener after this event.
	SuiteFinished(*TestingBatch)
}

TestingEventListener provides a way for users of scold to subscribe to the events produced by TestingBatch and to operate in a reactive fashion. The functions will stall the TestingBatch event loop, and thus could be not thread-safe.

type ThreadPool

type ThreadPool struct {
	// contains filtered or unexported fields
}

ThreadPool is a generic implementation of WorkerPool.

func NewThreadPool

func NewThreadPool(count int) *ThreadPool

NewThreadPool creates a thread pool with a definite limit to the number of concurrent operations permitted.

func (*ThreadPool) Execute

func (p *ThreadPool) Execute(task Runnable) error

Execute tries to find a free worker and assign task to it. If no worker is available, returns ErrNoWorkerAvailable.

func (*ThreadPool) WorkerCount

func (p *ThreadPool) WorkerCount() int

WorkerCount returns the number of threads in a pool. These threads may may not have a task assigned to them.

type Verdict

type Verdict int

Verdict represents a verdict asssigned by the judge.

const (
	OK Verdict = iota
	// Internal Error
	IE
	// Wrong Answer
	WA
	// Runtime Error
	RE
	// Time Limit
	TL
)

The set of all possible judge verdicts that can be assigned. The abbreviatons are due to competitive programming online judges.

type WorkerPool

type WorkerPool interface {
	Execute(task Runnable) error
	WorkerCount() int
}

WorkerPool abstracts the notion of a thread pool. It allows for interoperation with different thread pool libraries.

Directories

Path Synopsis
_examples
cmd

Jump to

Keyboard shortcuts

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