• Stars
    star
    986
  • Rank 46,429 (Top 1.0 %)
  • Language
    Go
  • License
    MIT License
  • Created over 10 years ago
  • Updated 22 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Exercism exercises in Go.

Exercism Go Track

Configlet Status Exercise Test Status

Exercism exercises in Go.

Issues/Feedback

⚠️ Please be aware that this repository currently does not accept community contributions. This blog post explains the details.

If you have any feedback or experience problems, you can bring them up in the Go section of the Exercism forum.

Development setup

If you work on this repository, you should follow some standard Go development practices. You should have a recent version of Go installed, ideally either the current release or previous release.

You will need a github account and you will need to fork exercism/go to your account. See GitHub Help if you are unfamiliar with the process. Clone your fork with the command: git clone https://github.com/<you>/go. Test your clone by cding to the go directory and typing bin/fetch-golangci-lint and then bin/run-tests. You should see tests pass for all exercises.

Note that unlike most other Go code, it is not necessary to clone this to your GOPATH. This is because this repo only imports from the standard library and isn't expected to be imported by other packages.

Your Go code should be formatted using the gofmt tool.

There is a misspelling tool. You can install and occasionally run it to find low hanging typo problems. #570 It's not added into CI since it could give false positives.

Contributing Guide

Please be familiar with the contributing guide in the docs repository. This describes some great ways to get involved. In particular, please read the Pull Request Guidelines before opening a pull request.

Exercism Go style

Let's walk through an example, non-existent, exercise, which we'll call fizzbuzz to see what could be included in its implementation.

Exercise configuration

An exercise is configured via an entry in the exercises array in config.json file. If fizzbuzz is an optional exercise, it would have an entry below the core exercises that might look like:

{
  "slug": "fizzbuzz",
  "uuid": "mumblety-peg-whatever",
  "core": false,
  "unlocked_by": "two-fer",
  "difficulty": 1,
  "topics": ["conditionals"]
}

See Exercism Docs: config.json for more info.

Exercise files: Overview

For any exercise you may see a number of files present in a directory under exercises/:

~/go/exercises/fizzbuzz
$ tree -a
.
├── cases_test.go
├── example.go
├── fizzbuzz.go
├── fizzbuzz_test.go
├── .meta
│   └── description.md
│   └── gen.go
│   └── hints.md
│   └── metadata.yml
└── README.md

This list of files can vary across exercises. Not all exercises use all of these files. Exercises originate their test data and README text from the Exercism problem-specification repository. This repository collects common information for all exercises across all tracks. However, should track-specific documentation need to be included with the exercise, files in an exercise's .meta/ directory can be used to override or augment the exercise's README.

Let's briefly describe each file:

  • cases_test.go - Contains generated test cases, using test data sourced from the problem-specifications repository. Only in some exercises. Automatically generated by .meta/gen.go.

  • example.go - An example solution for the exercise used to verify the test suite. Ignored by the exercism fetch command. See also ignored files.

  • fizzbuzz.go - A stub file, in some early exercises to give users a starting point.

  • fizzbuzz_test.go - The main test file for the exercise.

  • .meta/ - Contains files not to be included when a user fetches an exercise: See also ignored files.

  • .meta/description.md - Use to generate a track specific description of the exercise in the exercise's README.

  • .meta/gen.go - Generates cases_test.go when present. See also synchronizing exercises with problem specifications.

  • .meta/hints.md - Use to add track specific information in addition to the generic exercise's problem-specification description in the README.

  • .meta/metadata.yml - Track specific exercise metadata, overrides the exercise metadata from the problem-specifications repository.

In some exercises there can be extra files, for instance the series exercise contains extra test files.

Ignored files

When a user fetches an exercise, they do not need to get all the files within an exercise directory. For instance; the example.go files that contain an example solution, or the gen.go files used to generate an exercise's test cases. Therefore there are certain files and directories that are ignored when an exercise is fetched. These are:

  • The .meta directory and anything within it.
  • Any file that matches the ignore_pattern defined in config.json file. This currently matches any filename that contains the word example, unless it is followed by the word test, with any number of characters in between.

Example solutions

example.go is a reference solution. It is a valid solution that the CI (continuous integration) service can run tests against. Files with "example" in the file name are skipped by the exercism fetch command. Because of this, there is less need for this code to be a model of style, expression and readability, or to use the best algorithm. Examples can be plain, simple, concise, even naïve, as long as they are correct.

Stub files

Stub files, such as leap.go, are a starting point for solutions. Not all exercises need to have a stub file, only exercises early in the syllabus. By convention, the stub file for an exercise with slug exercise-slug must be named exercise_slug.go. This is because CI needs to delete stub files to avoid conflicting definitions.

Tests

The test file is fetched for the solver and deserves attention for consistency and appearance.

The leap exercise makes use of data-driven tests. Test cases are defined as data, then a test function iterates over the data. In this exercise, as they are generated, the test cases are defined in the cases_test.go file. The test function that iterates over this data is defined in the leap_test.go file. The cases_test.go file is generated by information found in problem-specifications using generators. To add additional test cases (e.g. test cases that only make sense for Go) add the test cases to <exercise>_test.go. An example of using additional test cases can be found in the exercise two-bucket.

Identifiers within the test function appear in actual-expected order as described at Useful Test Failures. Here the identifier observed is used instead of actual. That's fine. More common are words got and want. They are clear and short. Note Useful Test Failures is part of Code Review Comments. Really we like most of the advice on that page.

In Go we generally have all tests enabled and do not ask the solver to edit the test program, to enable progressive tests for example. t.Fatalf(), as seen in the leap_test.go file, will stop tests at the first failure encountered, so the solver is not faced with too many failures at once.

Testable examples

Some exercises can contain Example tests that document the exercise API. These examples are run alongside the standard exercise tests and will verify that the exercise API is working as expected. They are not required by all exercises and are not intended to replace the data-driven tests. They are most useful for providing examples of how an exercise's API is used. Have a look at the example tests in the clock exercise to see them in action.

Errors

We like errors in Go. It's not idiomatic Go to ignore invalid data or have undefined behavior. Sometimes our Go tests require an error return where other language tracks don't.

Benchmarks

In most test files there will also be benchmark tests, as can be seen at the end of the leap_test.go file. In Go, benchmarking is a first-class citizen of the testing package. We throw in benchmarks because they're interesting, and because it is idiomatic in Go to think about performance. There is no critical use for these though. Usually they will just bench the combined time to run over all the test data rather than attempt precise timings on single function calls. They are useful if they let the solver try a change and see a performance effect.

Synchronizing exercises with problem specifications

Some problems that are implemented in multiple tracks use the same inputs and outputs to define the test suites. Where the problem-specifications repository contains a canonical-data.json file with these inputs and outputs, we can generate the test cases programmatically. The problem-specifications repo also defines the instructions for the exercises, which are also shared across tracks and must also be synchronized.

Test structure

See the gen.go file in the leap exercise for an example of how this can be done.

Test case generators are named gen.go and are kept in a special .meta directory within each exercise that makes use of a test cases generator. This .meta directory will be ignored when a user fetches an exercise.

Whenever the shared JSON data changes, the test cases will need to be regenerated. The generator will first look for a local copy of the problem-specifications repository. If there isn't one it will attempt to get the relevant json data for the exercise from the problem-specifications repository on GitHub. The generator uses the GitHub API to find some information about exercises when it cannot find a local copy of problem-specifications. This can cause throttling issues if working with a large number of exercises. We therefore recommend using a local copy of the repository when possible (remember to keep it current 😄).

To use a local copy of the problem-specifications repository, make sure that it has been cloned into the same parent-directory as the go repository.

$ tree -L 1 .
.
├── problem-specifications
└── go

Adding a test generator to an exercise

For some exercises, a test generator is used to generate the cases_test.go file with the test cases based on information from problem-specifications. To add a new exercise generator to an exercise the following steps are needed:

  1. Create the file gen.go in the directory .meta of the exercise
  2. Add the following template to gen.go:
package main

import (
    "log"
    "text/template"
  
    "../../../../gen"
)

func main() {
	t, err := template.New("").Parse(tmpl)
	if err != nil {
		log.Fatal(err)
	}
	var j = map[string]interface{}{
              "property_1":  &[]Property1Case{},
              "property_2":  &[]Property2Case{},
	}
	if err := gen.Gen("<exercise-name>", j, t); err != nil {
		log.Fatal(err)
	}
}
  1. Insert the name of the exercise to the call of gen.Gen
  2. Add all values for the field property in canonical-data.json to the map j. canonical-data.json can be found at problem-specifications/exercises/<exercise-name>
  3. Create the needed structs for storing the test cases from canonical-data.json (you can for example use JSON-to-Go to convert the JSON to a struct)

NOTE: In some cases, the struct of the data in the input/expected fields is not the same for all test cases of one property. In those situations, an interface{} has to be used to represent the values for these fields. These interface{} values then need to be handled by the test generator. A common way to handle these cases is to create methods on the test case structs that perform type assertions on the interface{} values and return something more meaningful. These methods can then be referenced/called in the tmpl template variable. Examples of this can be found in the exercises forth or bowling.

  1. Add the variable tmpl to gen.go. This template will be used to create the cases_test.go file.

Example:

var tmpl = `package <package of exercise>

{{.Header}}

var testCases = []struct {
	description    string
	input          int
	expected       int       
}{ {{range .J.<property>}}
{
	description: {{printf "%q"  .Description}},
	input: {{printf "%d"  .Score}},
	expected: {{printf "%d"  .Score}},
},{{end}}
}
`
  1. Synchronize the test case using the exercise generator (as described in Synchronizing tests and instructions)
  2. Check the validity of cases_test.go
  3. Use the generated test cases in the <exercise>_test.go file
  4. Check if .meta/example.go passes all tests

Synchronizing tests and instructions

To keep track of which tests are implemented by the exercise the file .meta/tests.toml is used by configlet.

To synchronize the exercise with problem-specifications and to regenerate the tests, navigate into the go directory and perform the following steps:

  1. Synchronize your exercise with exercism/problem-specifications using configlet:
$ configlet sync --update -e <exercise>

configlet synchronizes the following parts, if an updated is needed:

  • docs: .docs/instructions.md, .docs/introduction.md
  • metadata: .meta/config.json
  • tests: .meta/tests.toml
  • filepaths: ./meta/config.json

For further instructions check out configlet.

  1. Run the test case generator to update <exercise>/cases_test.go:
$ GO111MODULE=off go run exercises/practice/<exercise>/.meta/gen.go

NOTE: If you see the error json: cannot unmarshal object into Go value of type []gen.Commit when running the generator you probably have been rate limited by GitHub. Try providing a GitHub access token with the flag -github_token="<Token>". Using the token will result in a higher rate limit. The token does not need any specific scopes as it is only used to fetch infos about commits.

You should see that some/all of the above files have changed. Commit the changes.

Synchronizing all exercises with generators

$ ./bin/run-generators <GitHub Access Token>

NOTE: If you see the error json: cannot unmarshal object into Go value of type []gen.Commit when running the generator you probably have been rate limited by GitHub. Make sure you provided the GitHub access token as first argument to the script as shown above. Using the token will result in a higher rate limit. The token does not need any specific scopes as it is only used to fetch infos about commits.

Managing the Go version

For an easy management of the Go version in the go.mod file in all exercises, we can use gomod-sync. This is a tool made in Go that can be seen in the gomod-sync/ folder.

To update all go.mod files according to the config file (gomod-sync/config.json) run:

$ cd gomod-sync && go run main.go update

To check all exercise go.mod files specify the correct Go version, run:

$ cd gomod-sync && go run main.go check

Pull requests

Pull requests are welcome. You forked, cloned, coded and tested and you have something good? Awesome! Use git to add, commit, and push to your repository. Checkout your repository on the web now. You should see your commit and the invitation to submit a pull request!

Click on that big green button. You have a chance to add more explanation to your pull request here, then send it. Looking at the exercism/go repository now instead of your own, you see this.

That inconspicuous orange dot is important! Hover over it (no, not on this image, on a real page) and you can see it's indicating that a CI build is in progress. After a few minutes (usually) that dot will turn green indicating that tests passed. If there's a problem, it comes up red:

This means you've still got work to do. Click on "details" to go to the CI build details. Look over the build log for clues. Usually error messages will be helpful and you can correct the problem.

Direction

Directions are unlimited. This code is fresh and evolving. Explore the existing code and you will see some new directions being tried. Your fresh ideas and contributions are welcome.

Go icon

The Go logo was designed by Renée French, and has been released under the Creative Commons 3.0 Attributions license.

More Repositories

1

exercism

Crowd-sourced code mentorship. Practice having thoughtful conversations about code.
7,312
star
2

python

Exercism exercises in Python.
Python
1,863
star
3

cli

A Go based command line tool for exercism.org.
Go
1,267
star
4

rust

Exercism exercises in Rust.
Rust
1,191
star
5

java

Exercism exercises in Java.
Java
686
star
6

elixir

Exercism exercises in Elixir.
Elixir
570
star
7

javascript

Exercism exercises in JavaScript.
JavaScript
560
star
8

ruby

Exercism exercises in Ruby.
Ruby
498
star
9

haskell

Exercism exercises in Haskell.
Haskell
450
star
10

website

The codebase for Exercism's website.
Ruby
405
star
11

problem-specifications

Shared metadata for exercism exercises.
Ruby
310
star
12

csharp

Exercism exercises in C#.
C#
276
star
13

c

Exercism exercises in C.
C
249
star
14

cpp

Exercism exercises in C++.
C++
226
star
15

website-copy

A repository for exercism's website's copy
HTML
198
star
16

DEPRECATED.javascript

Exercism exercises in JavaScript.
JavaScript
194
star
17

kotlin

Exercism exercises in Kotlin.
Kotlin
192
star
18

windows-installer

A project to download and install the latest version of Exercism Client for Windows depending on the architecture.
Pascal
190
star
19

v3

The work-in-progress project for developing v3 tracks
C#
170
star
20

typescript

Exercism exercises in TypeScript.
JavaScript
145
star
21

clojure

Exercism exercises in Clojure.
Clojure
144
star
22

php

Exercism exercises in PHP.
PHP
129
star
23

elm

Exercism exercises in Elm.
Elm
129
star
24

v2-website

Exercism — Code practice and mentorship for everyone.
Ruby
125
star
25

erlang

Exercism exercises in Erlang.
Erlang
124
star
26

scala

Exercism exercises in Scala.
Scala
115
star
27

fsharp

Exercism exercises in F#.
F#
106
star
28

swift

Exercism exercises in Swift.
Swift
101
star
29

bash

Exercism exercises in Bash.
Shell
92
star
30

ocaml

Exercism exercises in OCaml.
OCaml
91
star
31

legacy-docs

84
star
32

gleam

Exercism exercises in Gleam.
Gleam
81
star
33

common-lisp

Exercism exercises in Common Lisp.
Common Lisp
76
star
34

crystal

Exercism exercises in Crystal.
Crystal
70
star
35

abap

Exercism exercises in ABAP.
ABAP
63
star
36

julia

Exercism exercises in Julia.
Julia
61
star
37

nim

Exercism exercises in Nim.
Nim
51
star
38

dart

Exercism exercises in Dart.
Dart
50
star
39

DEPRECATED.exercism.rb

DEPRECATED. See http://github.com/exercism/cli for the new CLI.
Ruby
48
star
40

DEPRECATED.x-api

Application to serve exercism assignments
Ruby
46
star
41

emacs-lisp

Exercism exercises in Emacs Lisp.
Emacs Lisp
45
star
42

docs

Exercism's docs. View them at Exercism's website, not on GitHub.
42
star
43

racket

Exercism exercises in Racket.
Racket
39
star
44

zig

Exercism exercises in Zig.
Zig
38
star
45

pony

Exercism exercises in Pony.
Pony
38
star
46

discussions

For discussing things like future features, roadmap, priorities, and other things that are not directly action-oriented (yet).
37
star
47

powershell

Exercism exercises in Windows PowerShell.
PowerShell
35
star
48

scheme

Exercism exercises in Scheme.
Scheme
35
star
49

purescript

Exercism exercises in PureScript.
PureScript
35
star
50

hiring-frontend-developer

34
star
51

pr-commenter-action

The PR Commenter GitHub action posts comments on a PR that can vary depending on which files are being changed in the PR.
JavaScript
33
star
52

lua

Exercism exercises in Lua.
Lua
33
star
53

pharo-smalltalk

Exercism exercises in Pharo.
Smalltalk
33
star
54

delphi

Exercism exercises in Delphi Pascal.
Pascal
33
star
55

exalysis

Mentoring tool for the Go track on Exercism. Downloads students code, checks it and provides suggestions.
Go
33
star
56

DEPRECATED.rikki

A worker written in golang for automatically nitpicking exercism submissions.
Go
30
star
57

idris

Exercism exercises in Idris.
Idris
29
star
58

plsql

Exercism exercises in PL/SQL.
PLSQL
28
star
59

prolog

Exercism exercises in Prolog.
Prolog
27
star
60

elixir-analyzer

Elixir
27
star
61

perl5

Exercism exercises in Perl 5.
Perl
26
star
62

automated-analysis

An overview space for Automated Analysis on Exercism
26
star
63

sml

Exercism exercises in Standard ML.
Standard ML
24
star
64

lfe

Exercism exercises in Lisp Flavoured Erlang (LFE).
LFE
23
star
65

r

Exercism exercises in R.
R
23
star
66

gui

JavaScript
22
star
67

deprecated-mentors

Rust
22
star
68

fortran

Exercism exercises in Fortran.
Fortran
22
star
69

x86-64-assembly

Exercism exercises in x86-64 Assembly.
Assembly
22
star
70

raku

Exercism exercises in Raku
Raku
22
star
71

blog

Exercism's blog content
Shell
21
star
72

configlet

The official tool for managing Exercism language track repositories.
Nim
20
star
73

reasonml

Exercism exercises in ReasonML.
Reason
20
star
74

groovy

Exercism exercises in Groovy.
Groovy
19
star
75

red

Exercism exercises in Red.
Red
19
star
76

mips

Exercism exercises in MIPS Assembly.
Shell
19
star
77

vimscript

Exercism exercises in Vim script.
Vim Script
18
star
78

generic-track

Ruby
18
star
79

coq

Exercism exercises in Coq.
Shell
17
star
80

nix

Exercism exercises in Nix.
Shell
17
star
81

terraform

HCL
17
star
82

DEPRECATED.help.exercism.io

DEPRECATED. LEGACY. NO LONGER BEING WORKED ON.
JavaScript
17
star
83

d

Exercism exercises in D.
D
16
star
84

v2-configlet

Tool to assist in managing Exercism language tracks.
Go
16
star
85

coffeescript

Exercism exercises in CoffeeScript.
CoffeeScript
16
star
86

development-environment

Ruby
16
star
87

solidity

Exercism exercises in Solidity.
JavaScript
15
star
88

babashka

Exercism exercises in Babashka.
Clojure
14
star
89

javascript-analyzer

This is Exercism's automated analyzer for the JavaScript track.
TypeScript
14
star
90

interactive-cli-walkthrough

Ruby
13
star
91

DEPRECATED.v2-feedback

Please use https://github.com/exercism/exercism.io for reporting issues
13
star
92

ruby-analyzer

This is Exercism's automated analyzer for the Ruby track.
Ruby
13
star
93

csharp-analyzer

An analyser for C#
C#
12
star
94

cfml

Exercism exercises in CFML.
ColdFusion
12
star
95

gnu-apl

Exercism exercises in GNU APL.
APL
12
star
96

v3-beta

12
star
97

awk

Exercism exercises in AWK.
Shell
12
star
98

tracks-maintenance-dashboard

A dashboard for maintainers to understand the state of tracks
TypeScript
11
star
99

python-test-runner

Python
11
star
100

go-test-runner

Jupyter Notebook
11
star