coderunner

package module
v1.4.2 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2021 License: MIT Imports: 10 Imported by: 1

README

go-code-runner

GoDoc Go Report Card Build Status codecov

The library that implements the compiling and running of a code written in the Go programming language and running of a test case set for the compiled code (i.e. the executable file).

Features

  • saving of a code to a temporary file:
    • storing of the temporary file with the code to an individual temporary directory;
  • checking of package imports used in the code written in the Go programming language:
    • checking based on the set of allowed imports;
  • compiling of a code written in the Go programming language:
    • automatic importing of the packages used in the code (optionally);
    • checking of package imports used in the code (optionally):
      • checking based on the set of allowed imports;
    • enriching of an error of the external command running by an output from the stderr stream;
    • support for the running time management (via the Go context);
  • running of the compiled code (i.e. the executable file):
    • passing of a custom input as the stdin stream;
    • returning of an output from the stdout stream;
    • enriching of an error of the external command running by an output from the stderr stream;
    • support for the running time management (via the Go context);
  • running of a test case set for the compiled code (i.e. the executable file):
    • representation of a test case:
      • input;
      • expected output;
    • checking of an actual output in each test case:
      • returning of the sentinel errors:
        • failed running — it returns on a running error;
        • unexpected output — it returns when the expected and actual outputs do not match;
    • support for the running time management (via the Go context).

Installation

Prepare the directory:

$ mkdir --parents "$(go env GOPATH)/src/github.com/thewizardplusplus/"
$ cd "$(go env GOPATH)/src/github.com/thewizardplusplus/"

Clone this repository:

$ git clone https://github.com/thewizardplusplus/go-code-runner.git
$ cd go-code-runner

Install dependencies with the dep tool:

$ dep ensure -vendor-only

Example

coderunner.CheckImports() (success):

package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	mapset "github.com/deckarep/golang-set"
	coderunner "github.com/thewizardplusplus/go-code-runner"
	systemutils "github.com/thewizardplusplus/go-code-runner/system-utils"
)

func main() {
	const code = `
		package main

		import (
			"fmt"
			"log"
		)

		func main() {
			var x, y int
			if _, err := fmt.Scan(&x, &y); err != nil {
				log.Fatal(err)
			}

			fmt.Println(x + y)
		}
	`

	pathToCode, err := systemutils.SaveTemporaryText(code, ".go")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(filepath.Dir(pathToCode)) // nolint: errcheck

	err = coderunner.CheckImports(pathToCode, mapset.NewSet("fmt", "log"))
	fmt.Printf("%v\n", err)

	// Output:
	// <nil>
}

coderunner.CheckImports() (error):

package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	mapset "github.com/deckarep/golang-set"
	coderunner "github.com/thewizardplusplus/go-code-runner"
	systemutils "github.com/thewizardplusplus/go-code-runner/system-utils"
)

func main() {
	const code = `
		package main

		import (
			"fmt"
			"log"
		)

		func main() {
			var x, y int
			if _, err := fmt.Scan(&x, &y); err != nil {
				log.Fatal(err)
			}

			fmt.Println(x + y)
		}
	`

	pathToCode, err := systemutils.SaveTemporaryText(code, ".go")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(filepath.Dir(pathToCode)) // nolint: errcheck

	err = coderunner.CheckImports(pathToCode, mapset.NewSet("log"))
	fmt.Printf("%v\n", err)

	// Output:
	// disallowed import "fmt"
}

coderunner.CompileCode():

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"

	coderunner "github.com/thewizardplusplus/go-code-runner"
	systemutils "github.com/thewizardplusplus/go-code-runner/system-utils"
)

func main() {
	const code = `
		package main

		func main() {
			var x, y int
			fmt.Scan(&x, &y)

			fmt.Println(x + y)
		}
	`

	pathToCode, err := systemutils.SaveTemporaryText(code, ".go")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(filepath.Dir(pathToCode)) // nolint: errcheck

	pathToExecutable, err :=
		coderunner.CompileCode(context.Background(), pathToCode, nil)
	if err != nil {
		log.Fatal(err)
	}

	output, err :=
		systemutils.RunCommand(context.Background(), "2 3", pathToExecutable)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%q\n", output)

	// Output:
	// "5\n"
}

testrunner.RunTestCases() (success):

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"

	coderunner "github.com/thewizardplusplus/go-code-runner"
	systemutils "github.com/thewizardplusplus/go-code-runner/system-utils"
	testrunner "github.com/thewizardplusplus/go-code-runner/test-runner"
)

func main() {
	const code = `
		package main

		func main() {
			var x, y int
			fmt.Scan(&x, &y)

			fmt.Println(x + y)
		}
	`

	pathToCode, err := systemutils.SaveTemporaryText(code, ".go")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(filepath.Dir(pathToCode)) // nolint: errcheck

	pathToExecutable, err :=
		coderunner.CompileCode(context.Background(), pathToCode, nil)
	if err != nil {
		log.Fatal(err)
	}

	err = testrunner.RunTestCases(
		context.Background(),
		[]testrunner.TestCase{
			{Input: "5 12", ExpectedOutput: "17\n"},
			{Input: "23 42", ExpectedOutput: "65\n"},
		},
		func(ctx context.Context, input string) (output string, err error) {
			return systemutils.RunCommand(ctx, input, pathToExecutable)
		},
	)
	fmt.Printf("%v\n", err)

	// Output:
	// <nil>
}

testrunner.RunTestCases() (error):

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"

	coderunner "github.com/thewizardplusplus/go-code-runner"
	systemutils "github.com/thewizardplusplus/go-code-runner/system-utils"
	testrunner "github.com/thewizardplusplus/go-code-runner/test-runner"
)

func main() {
	const code = `
		package main

		func main() {
			var x, y int
			fmt.Scan(&x, &y)

			fmt.Println(x + y)
		}
	`

	pathToCode, err := systemutils.SaveTemporaryText(code, ".go")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(filepath.Dir(pathToCode)) // nolint: errcheck

	pathToExecutable, err :=
		coderunner.CompileCode(context.Background(), pathToCode, nil)
	if err != nil {
		log.Fatal(err)
	}

	err = testrunner.RunTestCases(
		context.Background(),
		[]testrunner.TestCase{
			{Input: "5 12", ExpectedOutput: "17\n"},
			{Input: "23 42", ExpectedOutput: "100\n"},
		},
		func(ctx context.Context, input string) (output string, err error) {
			return systemutils.RunCommand(ctx, input, pathToExecutable)
		},
	)
	fmt.Printf("%v\n", err)

	// Output:
	// unexpected output (input - "23 42"): expected - "100\n", actual - "65\n"
}

License

The MIT License (MIT)

Copyright © 2021 thewizardplusplus

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckImports

func CheckImports(pathToCode string, allowedImports mapset.Set) error

CheckImports ...

Example (Error)
package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	mapset "github.com/deckarep/golang-set"
	coderunner "github.com/thewizardplusplus/go-code-runner"

	systemutils "github.com/thewizardplusplus/go-code-runner/system-utils"
)

func main() {
	const code = `
		package main

		import (
			"fmt"
			"log"
		)

		func main() {
			var x, y int
			if _, err := fmt.Scan(&x, &y); err != nil {
				log.Fatal(err)
			}

			fmt.Println(x + y)
		}
	`

	pathToCode, err := systemutils.SaveTemporaryText(code, ".go")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(filepath.Dir(pathToCode)) // nolint: errcheck

	err = coderunner.CheckImports(pathToCode, mapset.NewSet("log"))
	fmt.Printf("%v\n", err)

}
Output:

disallowed import "fmt"
Example (Success)
package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	mapset "github.com/deckarep/golang-set"
	coderunner "github.com/thewizardplusplus/go-code-runner"

	systemutils "github.com/thewizardplusplus/go-code-runner/system-utils"
)

func main() {
	const code = `
		package main

		import (
			"fmt"
			"log"
		)

		func main() {
			var x, y int
			if _, err := fmt.Scan(&x, &y); err != nil {
				log.Fatal(err)
			}

			fmt.Println(x + y)
		}
	`

	pathToCode, err := systemutils.SaveTemporaryText(code, ".go")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(filepath.Dir(pathToCode)) // nolint: errcheck

	err = coderunner.CheckImports(pathToCode, mapset.NewSet("fmt", "log"))
	fmt.Printf("%v\n", err)

}
Output:

<nil>

func CompileCode

func CompileCode(
	ctx context.Context,
	pathToCode string,
	allowedImports mapset.Set,
) (pathToExecutable string, err error)

CompileCode ...

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"

	coderunner "github.com/thewizardplusplus/go-code-runner"

	systemutils "github.com/thewizardplusplus/go-code-runner/system-utils"
)

func main() {
	const code = `
		package main

		func main() {
			var x, y int
			fmt.Scan(&x, &y)

			fmt.Println(x + y)
		}
	`

	pathToCode, err := systemutils.SaveTemporaryText(code, ".go")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(filepath.Dir(pathToCode)) // nolint: errcheck

	pathToExecutable, err :=
		coderunner.CompileCode(context.Background(), pathToCode, nil)
	if err != nil {
		log.Fatal(err)
	}

	output, err :=
		systemutils.RunCommand(context.Background(), "2 3", pathToExecutable)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%q\n", output)

}
Output:

"5\n"

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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