├── go.sum
├── go.mod
├── static
├── favicon.ico
├── screenshot.png
└── index.html
├── test_browser.sh
├── design
├── figlayout.fig
├── figlayout.png
├── y.html
├── x.html
└── main.css
├── app
├── collections.go
├── flowControl.go
├── async.go
├── app_types.go
├── regex.go
├── recursion.go
├── binary.go
├── strings.go
├── methods.go
├── functions.go
├── collections_test.go
├── flowControl_test.go
├── operators.go
├── async_test.go
├── test_util.go
├── slices.go
├── binary_test.go
├── functions_test.go
├── recursion_test.go
├── methods_test.go
├── regex_test.go
├── strings_test.go
├── operators_test.go
└── slices_test.go
├── test_browser.cmd
├── .gitignore
├── .vscode
└── launch.json
├── .github
└── workflows
│ └── go.yml
├── test_all.sh
├── LICENSE
├── test_all.cmd
├── lessons.md
├── test_all.go
├── main.go
├── README.md
└── parse.go
/go.sum:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module dmh2000.xyz/goassessment
2 |
3 | go 1.18
4 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmh2000/go_assessment/HEAD/static/favicon.ico
--------------------------------------------------------------------------------
/test_browser.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # to specify port, use 'go run . -port=8081'
3 | go run .
4 |
--------------------------------------------------------------------------------
/design/figlayout.fig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmh2000/go_assessment/HEAD/design/figlayout.fig
--------------------------------------------------------------------------------
/design/figlayout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmh2000/go_assessment/HEAD/design/figlayout.png
--------------------------------------------------------------------------------
/static/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dmh2000/go_assessment/HEAD/static/screenshot.png
--------------------------------------------------------------------------------
/app/collections.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | func MakeMap(keys []interface{}, values []interface{}) map[interface{}]interface{} {
4 | m := make(map[interface{}]interface{})
5 |
6 | return m
7 | }
8 |
9 | func MakeList(data []interface{}) LinkedList {
10 | var list LinkedList
11 |
12 | return list
13 | }
14 |
--------------------------------------------------------------------------------
/test_browser.cmd:
--------------------------------------------------------------------------------
1 | @rem start the test web server
2 | @rem on Windows, 'go run' can conflict with some antivirus, so
3 | @rem this script will build the .exe first, then run it.
4 | @rem that seems to work around some of the antivirus tools
5 | @rem build step
6 | go build .
7 | @rem to specify a port, change this to 'goassessment.exe -port 8081'
8 | goassessment.exe
9 |
--------------------------------------------------------------------------------
/app/flowControl.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // write a function that receives a number as its argument;
4 | // if the number is divisible by 3, the function should return 'fizz';
5 | // if the number is divisible by 5, the function should return 'buzz';
6 | // if the number is divisible by 3 and 5, the function should return
7 | // 'fizzbuzz';
8 | //
9 | // otherwise the function should return the number as a string
10 | func fizzBuzz(num int) string {
11 | return ""
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 |
12 | # Output of the go coverage tool, specifically when used with LiteIDE
13 | *.out
14 |
15 | # Dependency directories (remove the comment below to include it)
16 | # vendor/
17 |
18 | # miscellaneous files to ignore
19 | js-assessment
20 | static/bootstrap-4.5.0-dist
21 | solutions
22 | __debug_bin
23 | goassessment
24 | results.txt
25 |
--------------------------------------------------------------------------------
/app/async.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | /**
4 | * Write a function, delayedCount, that takes two arguments:
5 | * a send channel 'c' and a receive channel 'q'.
6 | * The function is called within a goroutine.
7 | * The function should send the values 0 .. 4 on the send channel,
8 | * with a delay of 10 milliseconds or greater before sending each value.
9 | * When the function receives a message on the 'q' channel it should
10 | * send a final value of 10 then return
11 | */
12 | func delayedCount(c chan int, q chan int) {
13 | }
14 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "auto",
12 | "program": "${fileDirname}",
13 | "env": {},
14 | "args": []
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a golang project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3 |
4 | name: Go
5 |
6 | on:
7 | push:
8 | branches: [ "main", "collections" ]
9 | pull_request:
10 | branches: [ "main", "collections" ]
11 |
12 | jobs:
13 |
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - name: Set up Go
20 | uses: actions/setup-go@v4
21 | with:
22 | go-version: '1.20'
23 |
24 | - name: Test
25 | run: ./test_all.sh
26 |
--------------------------------------------------------------------------------
/app/app_types.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // Person definition
4 | type Person struct {
5 | age int
6 | name string
7 | }
8 |
9 | // PersonSlice slice of
10 | type PersonSlice []Person
11 |
12 | // Employee subtype of Person
13 | type Employee struct {
14 | p Person
15 | id int
16 | }
17 |
18 | // Notifier interface
19 | type Notifier interface {
20 | notify() string
21 | }
22 |
23 | // Dir recursive definition of a file structure
24 | type Dir struct {
25 | name string
26 | files []string
27 | dirs []Dir
28 | }
29 |
30 | type LinkedListElement struct {
31 | next *LinkedListElement
32 | value interface{}
33 | }
34 |
35 | type LinkedList struct {
36 | head *LinkedListElement
37 | }
38 |
--------------------------------------------------------------------------------
/app/regex.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // write a function that checks if the string contains a decimal number
4 | func containsNumber(s string) bool {
5 | return false
6 | }
7 |
8 | // write a function that checks if the string ends with a vowel
9 | func endsWithVowel(s string) bool {
10 | return false
11 | }
12 |
13 | // write a function that captures and returns the first string of 3 decimal digits
14 | func captureThreeNumbers(s string) string {
15 | return ""
16 | }
17 |
18 | // write a function that checks if the string matches a specified pattern
19 | func matchesPattern(s string) bool {
20 | return false
21 | }
22 |
23 | // write a function that checks if the string is a correctly-formatted monetary amounts in USD
24 | func isUSD(s string) bool {
25 | return false
26 | }
27 |
--------------------------------------------------------------------------------
/app/recursion.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // write a function that returns a list of files starting
4 | // from a directory. if dirName == "" start from top level.
5 | // else start from specified directory
6 | func listFiles(data Dir, dirName string) []string {
7 | return nil
8 | }
9 |
10 | // wrote a function that returns all permutations of the input array
11 | // hint : https://www.geeksforgeeks.org/heaps-algorithm-for-generating-permutations/
12 | func permute(arr []int) [][]int {
13 | return nil
14 | }
15 |
16 | // write a function that returns the fibonnaci number of n
17 | func fibonacci(n int) int {
18 | return -1
19 | }
20 |
21 | // write a function that returns an array of strings with all valid sets of n parens
22 | // hint : https://www.geeksforgeeks.org/print-all-combinations-of-balanced-parentheses/
23 | func validParentheses(n int) []string {
24 | return nil
25 | }
26 |
--------------------------------------------------------------------------------
/app/binary.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // write a function that returns the 0 or 1 value at the specified bit position
4 | func valueAtbit(num int, bit int) int {
5 | return -1
6 | }
7 |
8 | // write a function that returns the base 10 value of the binary string
9 | func base10(n string) int {
10 | return -1
11 | }
12 |
13 | // write a function that returns the binary value of num as a string
14 | func convertToBinary(num int) string {
15 | return ""
16 | }
17 |
18 | // write a function that returns the bitwise OR of the input values
19 | func bitwiseOr(x int, y int, z int) int {
20 | return -1
21 | }
22 |
23 | // write a function that returns the bitwise AND of the input values
24 | func bitwiseAnd(x int, y int, z int) int {
25 | return -1
26 | }
27 |
28 | // write a function that returns the bitwise XOR of the input values
29 | func bitwiseXor(x int, y int, z int) int {
30 | return -1
31 | }
32 |
--------------------------------------------------------------------------------
/app/strings.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // write a function that composes a string from arguments
4 | func composeString(a string, b string) string {
5 | return ""
6 | }
7 |
8 | // write a function that returns a string from a byte array
9 | func fromBytes(b []byte) string {
10 | return ""
11 | }
12 |
13 | // write a function that splits a string into words
14 | func splitString(s string) []string {
15 | return nil
16 | }
17 |
18 | // write a function that converts a string to Title Case
19 | func titleString(s string) string {
20 | return ""
21 | }
22 |
23 | func runesFromString(s string) []rune {
24 | return nil
25 | }
26 |
27 | // write a function that reduces adjacent repeated characters
28 | func reduceString(s string, count int) string {
29 | return ""
30 | }
31 |
32 | // write a function that wraps lines at a given number of columns without breaking works
33 | func wordWrap(s string, column int) string {
34 | return ""
35 | }
36 |
37 | // write a function that reverses the characters in a string
38 | func reverseString(s string) string {
39 | return ""
40 | }
41 |
--------------------------------------------------------------------------------
/app/methods.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import "container/list"
4 |
5 | // fix the Sort interface for PersonSlice
6 | // fix the Heap interface for PersonSlice sorted by ascending age
7 | // fix the List interface for PersonSlice
8 |
9 | // Len ...
10 | func (p PersonSlice) Len() int { return len(p) }
11 |
12 | // Less ...
13 | func (p PersonSlice) Less(i int, j int) bool {
14 | return false
15 | }
16 |
17 | // Swap ...
18 | func (p PersonSlice) Swap(i int, j int) {
19 |
20 | }
21 |
22 | // Push ...
23 | func (p *PersonSlice) Push(x interface{}) {
24 |
25 | }
26 |
27 | // Pop ...
28 | func (p *PersonSlice) Pop() interface{} {
29 | return nil
30 | }
31 |
32 | // write a function that creates and populates a list
33 | func populateList(p PersonSlice) *list.List {
34 | var a *list.List
35 | return a
36 | }
37 |
38 | // write a function that sends a notification for different types
39 | func sendNotification(n Notifier) string {
40 | return ""
41 | }
42 |
43 | // write a function that returns an integer value from an interface{}
44 | func intFromInterface(i interface{}) int {
45 | return -1
46 | }
47 |
--------------------------------------------------------------------------------
/test_all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 | echo "Operators"
5 | go test -v ./app/operators*.go ./app/test_util.go ./app/app_types.go
6 |
7 | echo "Flow Control"
8 | go test -v ./app/flowControl*.go ./app/test_util.go ./app/app_types.go
9 |
10 | echo "Strings"
11 | go test -v ./app/strings*.go ./app/test_util.go ./app/app_types.go
12 |
13 | echo "Slices"
14 | go test -v ./app/slices*.go ./app/test_util.go ./app/app_types.go
15 |
16 | echo "Binary"
17 | go test -v ./app/binary*.go ./app/test_util.go ./app/app_types.go
18 |
19 | echo "Functions"
20 | go test -v ./app/functions*.go ./app/test_util.go ./app/app_types.go
21 |
22 | echo "Methods"
23 | go test -v ./app/method*.go ./app/test_util.go ./app/app_types.go
24 |
25 | echo "Collections"
26 | go test -v ./app/collections*.go ./app/test_util.go ./app/app_types.go
27 |
28 | echo "Recursion"
29 | go test -v ./app/recursion*.go ./app/test_util.go ./app/app_types.go
30 |
31 | echo "Regexp"
32 | go test -v ./app/regex*.go ./app/test_util.go ./app/app_types.go
33 |
34 | echo "Async"
35 | go test -v ./app/async*.go ./app/test_util.go ./app/app_types.go
36 |
37 | exit 0
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 david howard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/functions.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // write a function that returns a function
4 | func fFunction(str string) func(string) string {
5 | return nil
6 | }
7 |
8 | // write a function that returns a slice of closures
9 | func fMakeClosures(fn func(x int) int, arr []int) []func() int {
10 | return nil
11 | }
12 |
13 | // write a function that implements a partial application
14 | func fPartial(
15 | fn func(a string, b string, c string) string,
16 | str1 string,
17 | str2 string,
18 | ) func(string) string {
19 | return nil
20 | }
21 |
22 | // write a function that creates a new array populated
23 | // with the result of calling the provided function
24 | func fMap(fn func(a int) int, arr []int) []int {
25 | return nil
26 | }
27 |
28 | // write a function that applies the reducer to a slice of integers
29 | func fReduce(fn func(acc int, val int) int, arr []int) int {
30 | return -1
31 | }
32 |
33 | // write a function that applies the filter to a slice of integers
34 | func fFilter(fn func(a int) bool, arr []int) []int {
35 | return nil
36 | }
37 |
38 | // write a function that handles a variadic argument
39 | func fVariadic(s ...int) string {
40 | return ""
41 | }
42 |
--------------------------------------------------------------------------------
/app/collections_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | // write a function that returns and empty map
8 | func TestCollectionMap(t *testing.T) {
9 | defer testPanic(t) // handle panics and syntax errors
10 | setTestInstance(t) // update test instance for logging
11 |
12 | keys := []interface{}{"a", "b", "c"}
13 | values := []interface{}{1, 2, 3}
14 | m := MakeMap(keys, values)
15 | if len(m) == 0 {
16 | t.Error("map is empty")
17 | }
18 |
19 | v, ok := m["a"]
20 | if !ok {
21 | t.Error("map does not contain key 'a'")
22 | }
23 | if v != 1 {
24 | t.Error("map['a'] is not 1")
25 | }
26 |
27 | v, ok = m["b"]
28 | if !ok {
29 | t.Error("map does not contain key 'b'")
30 | }
31 | if v != 2 {
32 | t.Error("map['b'] is not 2")
33 | }
34 |
35 | v, ok = m["c"]
36 | if !ok {
37 | t.Error("map does not contain key 'c'")
38 | }
39 | if v != 3 {
40 | t.Error("map['c'] is not 2")
41 | }
42 | }
43 |
44 | // write a function that returns a linked list supporting any type
45 | func TestCollectionLinkedList(t *testing.T) {
46 | defer testPanic(t) // handle panics and syntax errors
47 | setTestInstance(t) // update test instance for logging
48 |
49 | data := []interface{}{1, 2, 3, 4}
50 | list := MakeList(data)
51 | if list.head == nil {
52 | t.Error("list.head is nil")
53 | }
54 |
55 | node := list.head
56 |
57 | for _, v := range data {
58 | if node == nil {
59 | t.Error("node is nil")
60 | break
61 | }
62 |
63 | if node.value.(int) != v {
64 | t.Error("node.value is not", v)
65 | break
66 | }
67 | node = node.next
68 | }
69 |
70 | if node != nil {
71 | t.Error("node is not nil")
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/design/y.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Go-Assessment Test Results
6 |
7 |
progress 50%
8 |
passed 42
9 |
failed 15
10 |
11 |
12 |
Mon Dec 28 21:54:01 -0800 PST 2020 : ET 4.754 sec
13 |
14 |
15 |
16 |
17 |
18 |
Goal : You should be able to .......
19 |
FAIL
20 |
21 |
22 |
operator-test.go:15
23 |
this should be equal to 0
24 |
error
25 |
26 |
27 |
operator-test.go:19
28 |
this should be equal to 1
29 |
error
30 |
31 |
32 |
33 |
34 |
35 |
Goal : You should be able to .......
36 |
PASS
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/flowControl_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | // write a function that receives a number as its argument;
8 | // if the number is divisible by 3, the function should return 'fizz';
9 | // if the number is divisible by 5, the function should return 'buzz';
10 | // if the number is divisible by 3 and 5, the function should return
11 | // 'fizzbuzz';
12 | //
13 | // otherwise the function should return the number as a string
14 | func TestFlowControl(t *testing.T) {
15 | defer testPanic(t) // handle panics and syntax errors // handle panics and syntax errors
16 | setTestInstance(t) // update test instance for logging
17 |
18 | t.Log("GOAL: you should be able to conditionally branch your code")
19 |
20 | var num int
21 | var s string
22 |
23 | // prime number
24 | num = 97
25 |
26 | s = fizzBuzz(2)
27 | if s != "2" {
28 | t.Error(shouldBe(s, "2"))
29 | }
30 |
31 | s = fizzBuzz(101)
32 | if s != "101" {
33 | t.Error(shouldBe(s, "101"))
34 | }
35 |
36 | s = fizzBuzz(3)
37 | if s != "fizz" {
38 | t.Error(shouldBe(s, "fizz"))
39 | }
40 |
41 | s = fizzBuzz(6)
42 | if s != "fizz" {
43 | t.Error(shouldBe(s, "fizz"))
44 | }
45 |
46 | s = fizzBuzz(num * 3)
47 | if s != "fizz" {
48 | t.Error(shouldBe(s, "fizz"))
49 | }
50 |
51 | s = fizzBuzz(5)
52 | if s != "buzz" {
53 | t.Error(shouldBe(s, "buzz"))
54 | }
55 |
56 | s = fizzBuzz(10)
57 | if s != "buzz" {
58 | t.Error(shouldBe(s, "buzz"))
59 | }
60 |
61 | s = fizzBuzz(num * 5)
62 | if s != "buzz" {
63 | t.Error(shouldBe(s, "buzz"))
64 | }
65 |
66 | s = fizzBuzz(15)
67 | if s != "fizzbuzz" {
68 | t.Error(shouldBe(s, "fizzbuzz"))
69 | }
70 |
71 | s = fizzBuzz(num * 3 * 5)
72 | if s != "fizzbuzz" {
73 | t.Error(shouldBe(s, "fizzbuzz"))
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/operators.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // write a function that uses the addition operator
4 | func add(a int, b int) int {
5 | return -1
6 | }
7 |
8 | // write a function that uses the subtraction operator
9 | func subtract(a int, b int) int {
10 | return -1
11 | }
12 |
13 | // write a function that uses the multiplication operator
14 | func multiply(a int, b int) int {
15 | return -1
16 | }
17 |
18 | // write a function that uses the division operator
19 | func divide(a int, b int) int {
20 | return -1
21 | }
22 |
23 | // write a function that uses the remainder (integer modulo) operator
24 | func remainder(a int, b int) int {
25 | return -1
26 | }
27 |
28 | // write a function that uses the equal operator
29 | func equal(a int, b int) bool {
30 | return false
31 | }
32 |
33 | // write a function that uses the not equal operator
34 | func notEqual(a int, b int) bool {
35 | return false
36 | }
37 |
38 | // write a function that uses the less-than operator
39 | func lessThan(a int, b int) bool {
40 | return false
41 | }
42 |
43 | // write a function that uses the less-than or equal operator
44 | func lessThanEqual(a int, b int) bool {
45 | return false
46 | }
47 |
48 | // write a function that uses the greater-than operator
49 | func greaterThan(a int, b int) bool {
50 | return false
51 | }
52 |
53 | // write a function that uses the greater-than or equal operator
54 | func greaterThanEqual(a int, b int) bool {
55 | return false
56 | }
57 |
58 | // write a function that returns true if either argument is true
59 | func either(a bool, b bool) bool {
60 | return false
61 | }
62 |
63 | // write a function that returns true only if both arguments are true
64 | func both(a bool, b bool) bool {
65 | return false
66 | }
67 |
68 | // write a function that returns true only if both arguments are false
69 | func none(a bool, b bool) bool {
70 | return false
71 | }
72 |
--------------------------------------------------------------------------------
/test_all.cmd:
--------------------------------------------------------------------------------
1 | @echo "Operators" ./app/app_types.go 1>> results.txt 2>&1
2 | go test -v ./app/operators.go ./app/operators_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
3 | @echo "Flow Control" >results.txt
4 | go test -v ./app/flowControl.go ./app/flowControl_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>&1
5 | @echo "Strings" ./app/app_types.go 1>> results.txt 2>&1
6 | go test -v ./app/strings.go ./app/strings_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
7 | @echo "Slices" ./app/app_types.go 1>> results.txt 2>&1
8 | go test -v ./app/slices.go ./app/slices_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
9 | @echo "Binary" ./app/app_types.go 1>> results.txt 2>&1
10 | go test -v ./app/binary.go ./app/binary_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
11 | @echo "Functions" ./app/app_types.go 1>> results.txt 2>&1
12 | go test -v ./app/functions.go ./app/functions_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
13 | @echo "Recursion" ./app/app_types.go 1>> results.txt 2>&1
14 | go test -v ./app/recursion.go ./app/recursion_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
15 | @echo "Methods" ./app/app_types.go 1>> results.txt 2>&1
16 | go test -v ./app/methods.go ./app/methods_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
17 | @echo "Regexp" ./app/app_types.go 1>> results.txt 2>&1
18 | go test -v ./app/regex.go ./app/regex_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
19 | @echo "Async" ./app/app_types.go 1>> results.txt 2>&1
20 | go test -v ./app/async.go ./app/async_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
21 | @echo "Collections" ./app/app_types.go 1>> results.txt 2>&1
22 | go test -v ./app/collections.go ./app/collections_test.go ./app/test_util.go ./app/app_types.go 1>> results.txt 2>>&1
23 |
--------------------------------------------------------------------------------
/app/async_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "sync"
5 | "testing"
6 | "time"
7 | )
8 |
9 | /**
10 | * Write a function, delayedCount, that takes two arguments:
11 | * a send channel 'c' and a receive channel 'q'.
12 | * The function is called within a goroutine.
13 | * The function should send the values 0 .. 4 on the send channel,
14 | * with a delay of 10 milliseconds or greater before sending each value.
15 | * When the function receives a message on the 'q' channel it should
16 | * send a final value of 10 then return
17 | */
18 |
19 | func TestAsync(t *testing.T) {
20 | defer testPanic(t) // handle panics and syntax errors // handle panics and syntax errors
21 | setTestInstance(t) // update test instance for logging
22 |
23 | t.Log("GOAL: you should be able to send values to a channel with a delay")
24 |
25 | var c chan int
26 | var q chan int
27 | var wg sync.WaitGroup
28 |
29 | c = make(chan int)
30 | q = make(chan int, 2)
31 |
32 | wg.Add(2)
33 |
34 | go func() {
35 | var j int
36 | loop:
37 | for i := 0; i < 5; i++ {
38 | // record current time
39 | t0 := time.Now()
40 |
41 | // wait for response from go routine or timeout
42 | select {
43 | case j = <-c:
44 | if j != i {
45 | t.Error(shouldBe(j, i))
46 | }
47 | case <-time.After(2000 * time.Millisecond):
48 | t.Error("select statement timed out")
49 | break loop
50 | }
51 |
52 | // compute time difference
53 | t1 := time.Now()
54 | d := t1.Sub(t0)
55 | if d.Milliseconds() < 10 {
56 | t.Error("delay of ", d.Milliseconds(), "should be greater than 10ms")
57 | }
58 | }
59 | // signal quit
60 | q <- 0
61 |
62 | // check final value
63 | select {
64 | case j = <-c:
65 | if j != 10 {
66 | t.Error(shouldBe(j, 10))
67 | }
68 | case <-time.After(500 * time.Millisecond):
69 | t.Error("select statement timed out")
70 | }
71 |
72 | wg.Done()
73 | }()
74 |
75 | go func() {
76 | delayedCount(c, q)
77 | wg.Done()
78 | }()
79 |
80 | wg.Wait()
81 | }
82 |
--------------------------------------------------------------------------------
/lessons.md:
--------------------------------------------------------------------------------
1 | ## Lessons Learned From A Browser Based Go Test Driver
2 |
3 | The following is not news to experienced Go developers but it was to me.
4 |
5 | I hadn't written a Go web server app before, so going in it seemed simple. Then
6 | things started to go wrong. I learned that the Go runtime supports a form a recovery
7 | that many other runtimes don't.
8 |
9 | 1. Defer-Recover is the best thing going for a server app
10 | - [read more about it here](https://blog.golang.org/defer-panic-and-recover)
11 | - something most conventional runtimes don't support
12 | - Go allows recovery from panics, and even syntax errors in tests
13 | - in a node.js server app,for example, the recovery strategy is typically to let the process die and have it auto restarted
14 | - in a Go program, you can use defer-recover to catch panics and possibly continue
15 | - for example, if one web handler is crashing because some resource is missing, you can do a deferred recovery, log something and potentially disable the handler, while letting the rest of the program continue
16 | - or, just do a comprehensive log and restart
17 | 2. Go native testing works as though tests are compiled at runtime
18 | - syntax errors don't show up until a test is run
19 | - with a syntax error, output will not be what you expect
20 | - using a deferred recovery you can continue and log information even after a syntax error in a test
21 | 3. all tests should probably have a deferred recovery procedure to catch panics
22 | - a panic in a test won't kill the whole test sequence, just that test. it will be marked Fail
23 | - with a deferred recovery you can log why the panic occurred instead of a cryptic fail message
24 | 4. a simple web server may not be so simple
25 | - see the rules for handler patterns in servemux doc
26 | - '/' will handle anything starting with '/'
27 | - longer patterns are checked first
28 | - chrome requests favicon.ico even if there is no link in the html
29 | - should have deferred recoveries to catch panics in handlers
30 |
--------------------------------------------------------------------------------
/app/test_util.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "fmt"
5 | "path"
6 | "runtime"
7 | "testing"
8 | )
9 |
10 | // test output helper
11 | func shouldBe(a interface{}, b interface{}) string {
12 | return fmt.Sprintf("%v should be %v\n", a, b)
13 | }
14 |
15 | // check if two slices are equal
16 | func testIntSliceEqual(a []int, b []int) bool {
17 | if len(a) != len(b) {
18 | return false
19 | }
20 | for i := 0; i < len(a); i++ {
21 | if a[i] != b[i] {
22 | return false
23 | }
24 | }
25 |
26 | return true
27 |
28 | }
29 |
30 | // return index of target or -1 if not found
31 | func testIndexOfStrings(a []string, target string) int {
32 | for i, v := range a {
33 | if v == target {
34 | return i
35 | }
36 | }
37 | return -1
38 | }
39 |
40 | // find index of slice of integers in 2d array
41 | func testIndexOfIntSlice(a [][]int, target []int) int {
42 | for i, v := range a {
43 | // different lengths
44 | if len(v) != len(target) {
45 | return -1
46 | }
47 | // equal values
48 | for j := 0; j < len(v); j++ {
49 | if target[j] == v[j] {
50 | return i
51 | }
52 | }
53 | }
54 | return -1
55 | }
56 |
57 | // catch a panic in a test
58 | // must be called with defer
59 | func testPanic(t *testing.T) {
60 | r := recover()
61 | if r != nil {
62 | _, _, line, ok := runtime.Caller(3)
63 | if ok {
64 | t.Errorf("panic in solution at line %v : %v", line, r)
65 | } else {
66 | t.Errorf("panic in solution %v", r)
67 | }
68 | }
69 | }
70 |
71 | // a bit of kludgery to make testing.T.Log available to the
72 | // app skeletons for debugging. this is to avoid having to
73 | // pass the testing.T instance to each app function under test
74 | //
75 | //lint:ignore U1000 this is used by the app skeletons
76 | var testUtilT *testing.T
77 |
78 | // save the test instance
79 | func setTestInstance(t *testing.T) {
80 | testUtilT = t
81 | }
82 |
83 | // print a string to the test log
84 | //
85 | //lint:ignore U1000 this is used by the app skeletons
86 | func testLog(s string) {
87 | _, file, line, ok := runtime.Caller(1)
88 | if testUtilT != nil && ok {
89 | testUtilT.Logf("%v:%v:%v", path.Base(file), line, s)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/slices.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | // SLICES
4 | // For those functions that take a slice as input and return a slice,
5 | // you can either modify the input slice or make a copy for the return
6 | // the tests don't require that the input slice is not modified
7 |
8 | // write a function that returns the index of an item in a slice
9 | func indexOf(a []int, item int) int {
10 | return -1
11 | }
12 |
13 | // write a function that sums the values in a slice
14 | func sum(a []int) int {
15 | return -1
16 | }
17 |
18 | // write a function that removes all instances of a value from a slice
19 | func remove(a []int, item int) []int {
20 | return nil
21 | }
22 |
23 | // write a function that returns the value of the first element in a slice (wihtout removing it)
24 | func front(a []int) int {
25 | return -1
26 | }
27 |
28 | // write a function that returns the value of the last element in a slice (wihtout removing it)
29 | func back(a []int) int {
30 | return -1
31 | }
32 |
33 | // write a function that adds an item to the end of a slice
34 | func pushBack(a []int, item int) []int {
35 | return nil
36 | }
37 |
38 | // write a function that removes an item to the end of a slice
39 | func popBack(a []int) []int {
40 | return nil
41 | }
42 |
43 | // write a function that adds an item to the front of a slice
44 | func pushFront(a []int, item int) []int {
45 | return nil
46 | }
47 |
48 | // write a function that removes an item from the front of a slice
49 | func popFront(a []int) []int {
50 | return nil
51 | }
52 |
53 | // write a function that concatenates two slices
54 | func concat(a []int, b []int) []int {
55 | return nil
56 | }
57 |
58 | // write a function that adds an item to a slice at the specified index
59 | func insert(a []int, item int, index int) []int {
60 | return nil
61 | }
62 |
63 | // write a function that returns a count of matching items in a slice
64 | func count(a []int, item int) int {
65 | return -1
66 | }
67 |
68 | // write a function that finds duplicates in a slice
69 | func duplicates(a []int) []int {
70 | return nil
71 | }
72 |
73 | // write a function that squares all items in a slice
74 | func square(a []int) []int {
75 | return nil
76 | }
77 |
78 | // write a function that returns all the indices in a slice that matches an item
79 | func findAllOccurrences(a []int, item int) []int {
80 | return nil
81 | }
82 |
--------------------------------------------------------------------------------
/test_all.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 | "os/exec"
7 | "time"
8 | )
9 |
10 | func runTest(title string, test string, f *os.File) []string {
11 | var b []byte
12 | var s []string
13 | var app string = "./app/" + test + ".go"
14 | var tst string = "./app/" + test + "_test.go"
15 | var utl string = "./app/test_util.go"
16 | var typ string = "./app/app_types.go"
17 | cmd := exec.Command("go", "test", "-v", app, tst, utl, typ)
18 | b, _ = cmd.CombinedOutput()
19 |
20 | // save to results.txt
21 | // ignore errors
22 | if f != nil {
23 | _, err := f.Write(b)
24 | if err != nil {
25 | log.Printf("can't write results of %v to results.txt", title)
26 | }
27 | }
28 |
29 | // store the title
30 | s = make([]string, 0)
31 | s = append(s, "@start "+test)
32 | s = append(s, title+" : "+time.Now().Format("15:04:05"))
33 |
34 | // parse out lines in the output
35 | t := ""
36 | for _, c := range b {
37 | if c == 10 {
38 | s = append(s, t)
39 | t = ""
40 | } else {
41 | t += string(c)
42 | }
43 | }
44 | s = append(s, t)
45 |
46 | s = append(s, "@end")
47 |
48 | return s
49 | }
50 |
51 | // test command arguments
52 | type testPair struct {
53 | title string
54 | prog string
55 | }
56 |
57 | // list of all tests to execute
58 | var tests []testPair = []testPair{
59 | {"@Operators", "operators"},
60 | {"@Flow Control", "flowControl"},
61 | {"@Strings", "strings"},
62 | {"@Slices", "slices"},
63 | {"@Binary", "binary"},
64 | {"@Functions", "functions"},
65 | {"@Methods", "methods"},
66 | {"@Collections", "collections"},
67 | {"@Recursion", "recursion"},
68 | {"@Regexp", "regex"},
69 | {"@Async", "async"},
70 | }
71 |
72 | // run tests, capture all output and return it as a string
73 | func runAllTests(now time.Time) [][]string {
74 | var s [][]string
75 | var t []string
76 |
77 | f, err := os.OpenFile("./results.txt", os.O_CREATE|os.O_WRONLY, 0644)
78 | if err != nil {
79 | log.Println("Can't Open results.txt for writing")
80 | f = nil
81 | }
82 |
83 | if f != nil {
84 | // output date/time stamp
85 | f.WriteString(now.Local().Format("Mon Jan 2 15:04:05 -0700 MST 2006") + "\n")
86 | }
87 |
88 | s = make([][]string, 0)
89 | for _, p := range tests {
90 | t = runTest(p.title, p.prog, f)
91 | s = append(s, t)
92 | }
93 |
94 | f.Close()
95 |
96 | return s
97 | }
98 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "html/template"
7 | "log"
8 | "net/http"
9 | "regexp"
10 | "time"
11 | )
12 |
13 | // anything but / and /index.html
14 | func handle404(w http.ResponseWriter, r *http.Request) {
15 | log.Println("404:", r.RequestURI)
16 | w.WriteHeader(404)
17 | fmt.Fprint(w, "404 not found")
18 | }
19 |
20 | // something broke
21 | func handle500(w http.ResponseWriter, r *http.Request, err string) {
22 | log.Println(err)
23 | w.WriteHeader(500)
24 | fmt.Fprintf(w, "500 server error")
25 | }
26 |
27 | // page template
28 | type testPage struct {
29 | Body template.HTML
30 | }
31 |
32 | // execute tests and render the results as html
33 | func handleTests(w http.ResponseWriter, r *http.Request) {
34 | defer func() {
35 | rec := recover()
36 | if rec != nil {
37 | handle500(w, r, "error in handleTests")
38 | }
39 | }()
40 |
41 | // allow only / or /index.html
42 | var m bool
43 | m, _ = regexp.MatchString(`^/$|^/index.html$`, r.RequestURI)
44 | if !m {
45 | handle404(w, r)
46 | return
47 | }
48 |
49 | log.Println("req:", r.RequestURI)
50 |
51 | var tests [][]string
52 | var page testPage
53 |
54 | log.Println("Tests Started")
55 |
56 | // execute the tests
57 | t0 := time.Now()
58 | tests = runAllTests(t0)
59 | t1 := time.Now()
60 | // tests finished
61 |
62 | // elapsed time
63 | et := t1.Sub(t0)
64 |
65 | // print time for tests to execute
66 | log.Println("Tests Finished:", et)
67 |
68 | // compile the test results into a string
69 | timestamp := fmt.Sprintf("%s : ET %.3f sec", t0.Local().Format("Mon Jan 2 15:04:05 -0700 MST 2006"), et.Seconds())
70 |
71 | // timestamp := t0.Local().Format("Mon Jan 2 15:04:05 -0700 MST 2006") + ": ET " + (t1.Sub(t0).Seconds())
72 | page.Body = template.HTML(TestToHTML(timestamp, tests))
73 |
74 | // create the template
75 | t, err := template.ParseFiles("static/index.html")
76 | if err != nil {
77 | handle500(w, r, err.Error())
78 | return
79 | }
80 |
81 | // execute the template
82 | err = t.Execute(w, &page)
83 | if err != nil {
84 | log.Println(err)
85 | handle500(w, r, err.Error())
86 | }
87 | }
88 |
89 | func main() {
90 | // get specified port
91 | port := flag.String("port", "8080", "http://ipaddr:port")
92 | flag.Parse()
93 |
94 | // handle the test
95 | http.HandleFunc("/index.html", handleTests)
96 | http.HandleFunc("/", handleTests)
97 |
98 | // handle static files
99 | // fs := http.FileServer(http.Dir("static/"))
100 | // http.Handle("/static/",http.StripPrefix("/static/",fs))
101 |
102 | // run the server
103 | log.Println("listening on :", *port)
104 | log.Fatal(http.ListenAndServe(":"+*port, nil))
105 | }
106 |
--------------------------------------------------------------------------------
/app/binary_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | // write a function that returns the 0 or 1 value at the specified bit position
8 | func TestValueAtBit(t *testing.T) {
9 | defer testPanic(t) // handle panics and syntax errors // handle panics and syntax errors
10 | setTestInstance(t) // update test instance for logging
11 |
12 | t.Log("GOAL: you should be able to find the value of a given bit")
13 | var bit int
14 |
15 | // first bit is at bit position 0
16 | bit = valueAtbit(1, 0)
17 | if bit != 1 {
18 | t.Error(shouldBe(bit, 1))
19 | }
20 |
21 | bit = valueAtbit(128, 7)
22 | if bit != 1 {
23 | t.Error(shouldBe(bit, 1))
24 | }
25 |
26 | bit = valueAtbit(65, 0)
27 | if bit != 1 {
28 | t.Error(shouldBe(bit, 0))
29 | }
30 |
31 | bit = valueAtbit(65, 6)
32 | if bit != 1 {
33 | t.Error(shouldBe(bit, 1))
34 | }
35 |
36 | bit = valueAtbit(128, 1)
37 | if bit != 0 {
38 | t.Error(shouldBe(bit, 0))
39 | }
40 |
41 | }
42 |
43 | // write a function that returns the base 10 integer value of the binary string
44 | func TestBase10(t *testing.T) {
45 | defer testPanic(t) // handle panics and syntax errors // handle panics and syntax errors
46 | setTestInstance(t) // update test instance for logging
47 |
48 | t.Log("GOAL: you should be able to return the base10 representation of a binary string")
49 | val := base10("11000000")
50 | if val != 192 {
51 | t.Error(shouldBe(val, 192))
52 | }
53 | }
54 |
55 | // write a function that converts the int value to a binary string
56 | func TestConvertoBinary(t *testing.T) {
57 | defer testPanic(t) // handle panics and syntax errors // handle panics and syntax errors
58 | setTestInstance(t) // update test instance for logging
59 |
60 | t.Log("GOAL: you should be able to convert an eight-bit number to a binary string")
61 | var bin string
62 |
63 | bin = convertToBinary(128)
64 | if bin != "10000000" {
65 | t.Error(shouldBe(bin, "10000000"))
66 | }
67 |
68 | bin = convertToBinary(65)
69 | if bin != "1000001" {
70 | t.Error(shouldBe(bin, "1000001"))
71 | }
72 | }
73 |
74 | // write a function that returns the bitwise OR of the input values
75 | func TestBitwiseOr(t *testing.T) {
76 | defer testPanic(t) // handle panics and syntax errors // handle panics and syntax errors
77 | setTestInstance(t) // update test instance for logging
78 |
79 | t.Log("GOAL: you should be able to use the bitwise OR operator")
80 |
81 | r := 770047
82 | x := bitwiseOr(0x12345, 0x33333, 0xabcde)
83 | if x != r {
84 | t.Error(shouldBe(x, r))
85 | }
86 | }
87 |
88 | // write a function that returns the bitwise AND of the input values
89 | func TestBitwiseAnd(t *testing.T) {
90 | defer testPanic(t) // handle panics and syntax errors // handle panics and syntax errors
91 | setTestInstance(t) // update test instance for logging
92 |
93 | t.Log("GOAL: you should be able to use the bitwise AND operator")
94 |
95 | r := 8192
96 | x := bitwiseAnd(0x12345, 0x33333, 0xabcde)
97 | if x != r {
98 | t.Error(shouldBe(x, r))
99 | }
100 |
101 | }
102 |
103 | // write a function that returns the bitwise XOR of the input values
104 | func TestBitwiseXor(t *testing.T) {
105 | defer testPanic(t) // handle panics and syntax errors // handle panics and syntax errors
106 | setTestInstance(t) // update test instance for logging
107 |
108 | t.Log("GOAL: you should be able to use the bitwise XOR operator")
109 |
110 | r := 568488
111 | x := bitwiseXor(0x12345, 0x33333, 0xabcde)
112 | if x != r {
113 | t.Error(shouldBe(x, r))
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Go Assessment
7 |
8 |
13 |
14 |
15 |
21 |
26 |
31 |
36 |
37 |
96 |
97 |
108 |
109 |
110 |
111 |
112 |
129 |
{{.Body}}
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/app/functions_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "testing"
7 | )
8 |
9 | // write a function that returns a function
10 | func TestFunction(t *testing.T) {
11 | defer testPanic(t) // handle panics and syntax errors
12 | setTestInstance(t) // update test instance for logging
13 |
14 | t.Log("GOAL: you should be able to return a function from a function")
15 | var s string
16 | var r string
17 |
18 | f := fFunction("Hello")
19 | if f == nil {
20 | t.Error("fFunction returned nil")
21 | return
22 | }
23 |
24 | // check type of function
25 | q := fmt.Sprintf("%T", f)
26 | if q != "func(string) string" {
27 | t.Error(shouldBe(q, "func(string) string"))
28 | }
29 |
30 | //
31 | r = "Hello, world"
32 | s = f("world")
33 | if s != r {
34 | t.Error(shouldBe(s, r))
35 | }
36 |
37 | f = fFunction("Hai")
38 | if f == nil {
39 | t.Error("fFunction returned nil")
40 | }
41 |
42 | r = "Hai, can i haz funxtion?"
43 | s = f("can i haz funxtion?")
44 | if s != r {
45 | t.Error(shouldBe(s, r))
46 | }
47 | }
48 |
49 | // write a function that returns a slice of closures
50 | func TestClosures(t *testing.T) {
51 | defer testPanic(t) // handle panics and syntax errors
52 | setTestInstance(t) // update test instance for logging
53 |
54 | t.Log("GOAL: you should be able to use closures")
55 |
56 | arr := []int{rand.Int(), rand.Int(), rand.Int()}
57 | square := func(x int) int { return x * x }
58 |
59 | funcs := fMakeClosures(square, arr)
60 | if len(funcs) != len(arr) {
61 | t.Error(shouldBe(len(funcs), len(arr)))
62 | }
63 |
64 | for i, f := range funcs {
65 | u := f()
66 | v := square(arr[i])
67 | if u != v {
68 | t.Error(shouldBe(u, v))
69 | }
70 | }
71 | }
72 |
73 | // write a function that implements a partial application
74 | func TestPartial(t *testing.T) {
75 | defer testPanic(t) // handle panics and syntax errors
76 | setTestInstance(t) // update test instance for logging
77 |
78 | t.Log("GOAL: you should be able to create a 'partial' function")
79 |
80 | f := func(a string, b string, c string) string {
81 | return a + ", " + b + c
82 | }
83 |
84 | g := fPartial(f, "Hello", "Ellie")
85 | // check valid return
86 | if g == nil {
87 | t.Error("fPartial returned nil")
88 | return
89 | }
90 |
91 | // check type of function
92 | q := fmt.Sprintf("%T", g)
93 | if q != "func(string) string" {
94 | t.Error(shouldBe(q, "func(string) string"))
95 | }
96 |
97 | // invoke it
98 | s := g("!!!")
99 |
100 | // check function output
101 | r := "Hello, Ellie!!!"
102 | if s != r {
103 | t.Error(shouldBe(s, r))
104 | }
105 |
106 | }
107 |
108 | // write a function that creates a new array populated
109 | // with the result of calling the provided function
110 | func TestMap(t *testing.T) {
111 | defer testPanic(t) // handle panics and syntax errors
112 | setTestInstance(t) // update test instance for logging
113 |
114 | t.Log("GOAL: you should be able to implement a MAP function")
115 |
116 | square := func(x int) int { return x * x }
117 |
118 | a := []int{1, 2, 3, 4, 5}
119 | b := []int{1, 4, 9, 16, 25}
120 |
121 | c := fMap(square, a)
122 | if len(c) != len(a) {
123 | t.Error(shouldBe(len(c), len(a)))
124 | }
125 |
126 | for i := 0; i < len(b); i++ {
127 | if c[i] != b[i] {
128 | t.Error(shouldBe(c[i], b[i]))
129 | }
130 | }
131 | }
132 |
133 | // write a function that applies the reducer to a slice of integers
134 | func TestReduce(t *testing.T) {
135 | defer testPanic(t) // handle panics and syntax errors
136 | setTestInstance(t) // update test instance for logging
137 |
138 | t.Log("GOAL: you should be able to implement a REDUCE function")
139 |
140 | sum := func(acc int, val int) int {
141 | acc += val
142 | return acc
143 | }
144 |
145 | a := []int{1, 2, 3, 4, 5}
146 |
147 | b := fReduce(sum, a)
148 | if b != 15 {
149 | t.Error(shouldBe(b, 15))
150 | }
151 | }
152 |
153 | // write a function that applies the filter to a slice of integers
154 | func TestFilter(t *testing.T) {
155 | defer testPanic(t) // handle panics and syntax errors
156 | setTestInstance(t) // update test instance for logging
157 |
158 | t.Log("GOAL: you should be able to implement a FILTER function")
159 |
160 | // condition
161 | odd := func(v int) bool { return (v % 2) == 1 }
162 |
163 | a := []int{1, 2, 3, 4, 5}
164 | b := []int{1, 3, 5}
165 |
166 | c := fFilter(odd, a)
167 | if len(c) != len(b) {
168 | t.Error(shouldBe(len(c), len(b)))
169 | }
170 |
171 | for i := 0; i < len(b); i++ {
172 | if c[i] != b[i] {
173 | t.Error(shouldBe(c[i], b[i]))
174 | }
175 | }
176 | }
177 |
178 | // write a function that handles a variadic argument
179 | func TestVariadic(t *testing.T) {
180 | defer testPanic(t) // handle panics and syntax errors
181 | setTestInstance(t) // update test instance for logging
182 |
183 | t.Log("GOAL: you should be able to implement a variadic function")
184 |
185 | a := []int{1, 2, 3, 4, 5}
186 |
187 | s := fVariadic(a...)
188 | r := "1,2,3,4,5"
189 | if s != r {
190 | t.Error(shouldBe(s, r))
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/app/recursion_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | // defined in app_types.go
8 | // Dir recursive definition of a file structure
9 | // type Dir struct {
10 | // name string
11 | // files []string
12 | // dirs []Dir
13 | // }
14 |
15 | var fileData = Dir{
16 | name: "app",
17 | files: []string{"index.html"},
18 | dirs: []Dir{
19 | {
20 | name: "js",
21 | files: []string{"main.js", "app.js", "misc.js"},
22 | dirs: []Dir{
23 | {
24 | name: "vendor",
25 | files: []string{"jquery.js", "underscore.js"},
26 | },
27 | {
28 | name: "css",
29 | files: []string{"reset.css", "main.css"},
30 | dirs: []Dir{},
31 | },
32 | },
33 | },
34 | },
35 | }
36 |
37 | // write a function that returns a list of files starting at the top level directory
38 | func TestListFiles(t *testing.T) {
39 | defer testPanic(t) // handle panics and syntax errors
40 | setTestInstance(t) // update test instance for logging
41 |
42 | t.Log("GOAL: you should be able to return a list of files starting at the top level directory")
43 | var index int
44 |
45 | // passing in empty string "" means list all files from the top level
46 | var result = listFiles(fileData, "")
47 | if len(result) != 8 {
48 | t.Error(shouldBe(len(result), 8))
49 | }
50 |
51 | index = testIndexOfStrings(result, "index.html")
52 | if index < 0 {
53 | t.Error(index, "should be >= 0")
54 | }
55 |
56 | index = testIndexOfStrings(result, "main.js")
57 | if index < 0 {
58 | t.Error(index, "should be >= 0")
59 | }
60 |
61 | index = testIndexOfStrings(result, "notfound.js")
62 | if index > 0 {
63 | t.Error(shouldBe(index, -1))
64 | }
65 | }
66 |
67 | // wrote a function that returns a list of all files starting at the named subdirectory
68 | func TestListDir(t *testing.T) {
69 | defer testPanic(t) // handle panics and syntax errors
70 | setTestInstance(t) // update test instance for logging
71 |
72 | t.Log("GOAL: you should be able to return a list of files starting at the named subdirectory")
73 | var index int
74 |
75 | var result = listFiles(fileData, "js")
76 | if len(result) != 7 {
77 | t.Error(shouldBe(len(result), 7))
78 | }
79 |
80 | index = testIndexOfStrings(result, "main.js")
81 | if index < 0 {
82 | t.Error(index, "should be >= 0")
83 | }
84 |
85 | index = testIndexOfStrings(result, "index.html")
86 | if index > 0 {
87 | t.Error(shouldBe(index, -1))
88 | }
89 | }
90 |
91 | var permData = []int{1, 2, 3, 4}
92 |
93 | var permutations = [][]int{
94 | {1, 2, 3, 4},
95 | {1, 2, 4, 3},
96 | {1, 3, 2, 4},
97 | {1, 3, 4, 2},
98 | {1, 4, 2, 3},
99 | {1, 4, 3, 2},
100 | {2, 1, 3, 4},
101 | {2, 1, 4, 3},
102 | {2, 3, 1, 4},
103 | {2, 3, 4, 1},
104 | {2, 4, 1, 3},
105 | {2, 4, 3, 1},
106 | {3, 1, 2, 4},
107 | {3, 1, 4, 2},
108 | {3, 2, 1, 4},
109 | {3, 2, 4, 1},
110 | {3, 4, 1, 2},
111 | {3, 4, 2, 1},
112 | {4, 1, 2, 3},
113 | {4, 1, 3, 2},
114 | {4, 2, 1, 3},
115 | {4, 2, 3, 1},
116 | {4, 3, 1, 2},
117 | {4, 3, 2, 1},
118 | }
119 |
120 | // write a function that returns the fibonnaci number of n
121 | // Note : order of return values is arbitrary
122 | func TestPermute(t *testing.T) {
123 | defer testPanic(t) // handle panics and syntax errors
124 | setTestInstance(t) // update test instance for logging
125 |
126 | t.Log("GOAL: you should be able to return the permutations of an array")
127 |
128 | result := permute(permData)
129 |
130 | // length of answers should be the same
131 | if len(result) != len(permutations) {
132 | t.Error(shouldBe(len(result), len(permutations)))
133 | }
134 |
135 | // check for all permutations, order not required
136 | for _, target := range result {
137 | index := testIndexOfIntSlice(permutations, target)
138 | if index < 0 {
139 | t.Error("index of ", target, " should be >=0 ")
140 | }
141 | }
142 | }
143 |
144 | // write a function that returns the fibonnaci number of n
145 | func TestFibonacci(t *testing.T) {
146 | defer testPanic(t) // handle panics and syntax errors
147 | setTestInstance(t) // update test instance for logging
148 |
149 | t.Log("GOAL: you should be able to return the nth number in a fibonacci sequence")
150 | var fib int
151 |
152 | fib = fibonacci(2)
153 | if fib != 1 {
154 | t.Error(shouldBe(fib, 1))
155 | }
156 | fib = fibonacci(6)
157 | if fib != 8 {
158 | t.Error(shouldBe(fib, 8))
159 | }
160 | }
161 |
162 | // write a function that returns an array of strings with all valid sets of n parens
163 | func TestValidParens(t *testing.T) {
164 | defer testPanic(t) // handle panics and syntax errors
165 | setTestInstance(t) // update test instance for logging
166 |
167 | t.Log("GOAL: you should be able to return the set of all valid combinations of n pairs of parentheses.")
168 |
169 | r := []string{"((()))", "(()())", "(())()", "()(())", "()()()"}
170 | s := validParentheses(3)
171 | if len(s) != len(r) {
172 | t.Error(shouldBe(len(s), len(r)))
173 | }
174 |
175 | for i := 0; i < len(r); i++ {
176 | index := testIndexOfStrings(s, r[i])
177 | if index < 0 {
178 | t.Error(s, "should contain", r[i])
179 | }
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/app/methods_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "container/heap"
5 | "container/list"
6 | "fmt"
7 | "sort"
8 | "testing"
9 | )
10 |
11 | // defined in app_types.go
12 | // type Person struct {
13 | // age int
14 | // name string
15 | // }
16 |
17 | // type PersonSlice []Person
18 |
19 | // type Employee struct {
20 | // p Person
21 | // id int
22 | // }
23 |
24 | // Sort : implement Sort for PersonSlice
25 | func TestMethodsSort(t *testing.T) {
26 | defer testPanic(t) // handle panics and syntax errors
27 | setTestInstance(t) // update test instance for logging
28 |
29 | t.Log("GOAL: you should be able to implement the Sort interface for a specified type")
30 |
31 | // unsorted
32 | p := PersonSlice{
33 | {20, "January"}, {18, "April"}, {45, "June"}, {30, "August"},
34 | }
35 | // sorted
36 | q := PersonSlice{
37 | {18, "April"}, {20, "January"}, {30, "August"}, {45, "June"},
38 | }
39 |
40 | // =========================================
41 | // fix the sort implementation in methods.go
42 | // sort by age
43 | // =========================================
44 | sort.Sort(p)
45 |
46 | for i := 0; i < len(p); i++ {
47 | if p[i] != q[i] {
48 | t.Error(shouldBe(p[i], q[i]))
49 | }
50 | }
51 | }
52 |
53 | // fix the Heap interface for PersonSlice sorted by ascending age
54 | func TestMethodsHeap(t *testing.T) {
55 | defer testPanic(t) // handle panics and syntax errors
56 | setTestInstance(t) // update test instance for logging
57 |
58 | t.Log("GOAL: you should be able to implement the Heap interface for a specified type")
59 |
60 | p := PersonSlice{
61 | {20, "January"}, {18, "April"}, {45, "June"}, {30, "August"},
62 | }
63 | q := PersonSlice{
64 | {18, "April"}, {20, "January"}, {30, "August"}, {45, "June"},
65 | }
66 |
67 | // ===================================================
68 | // fix the container/heap implementation in methods.go
69 | // sort by age ascending
70 | // ====================================================
71 | // init the heap
72 | h := &PersonSlice{}
73 | heap.Init(h)
74 |
75 | // load the heap
76 | for i := 0; i < len(p); i++ {
77 | heap.Push(h, p[i])
78 | }
79 |
80 | // pop in ascending order by age
81 | for i := 0; i < len(q); i++ {
82 | r := heap.Pop(h)
83 | if r == nil {
84 | t.Error("heap should not be empty")
85 | return
86 | }
87 | if r.(Person).age != q[i].age {
88 | t.Error(shouldBe(r, q[i]))
89 | }
90 | }
91 |
92 | // should be empty
93 | if h.Len() > 0 {
94 | t.Error("heap should be empty")
95 | }
96 | }
97 |
98 | // write a function that creates and populates a list
99 | func TestMethodsList(t *testing.T) {
100 | defer testPanic(t) // handle panics and syntax errors
101 | setTestInstance(t) // update test instance for logging
102 |
103 | t.Log("GOAL: you should be able to create and populate a list")
104 | var p *list.List
105 | var e *list.Element
106 |
107 | q := PersonSlice{
108 | {18, "April"}, {20, "January"}, {30, "August"}, {45, "June"},
109 | }
110 |
111 | // populate a list in order of increasing age
112 | p = populateList(q)
113 | if p == nil {
114 | t.Error("should return a list")
115 | return
116 | }
117 |
118 | e = p.Front()
119 | i := 0
120 | for e != nil {
121 | if i >= len(q) {
122 | t.Error("list is too long")
123 | }
124 | if e.Value.(Person).name != q[i].name {
125 | t.Error(shouldBe(e.Value.(Person), q[i]))
126 | }
127 | i++
128 | e = e.Next()
129 | }
130 | }
131 |
132 | // defined in app_types.go
133 | // Notifier interface
134 | // type Notifier interface {
135 | // notify() string
136 | // }
137 |
138 | func (p Person) notify() string {
139 | return fmt.Sprintf("new mail for %s", p.name)
140 | }
141 |
142 | func (e Employee) notify() string {
143 | return fmt.Sprintf("new mail for %v:%s", e.id, e.p.name)
144 | }
145 |
146 | func TestMethodsNotify(t *testing.T) {
147 | defer testPanic(t) // handle panics and syntax errors
148 | setTestInstance(t) // update test instance for logging
149 |
150 | t.Log("GOAL: you should be able to use an interface with different types")
151 | var s string
152 | var r string
153 |
154 | p := Person{name: "October", age: 99}
155 | e := Employee{p: Person{name: "September", age: 31}, id: 12345}
156 |
157 | // =========================
158 | // your function
159 | s = sendNotification(e)
160 | r = "new mail for 12345:September"
161 | if s != r {
162 | t.Error(shouldBe(s, r))
163 | }
164 |
165 | // =========================
166 | // your function
167 | s = sendNotification(p)
168 | r = "new mail for October"
169 | if s != r {
170 | t.Error(shouldBe(s, r))
171 | }
172 | }
173 |
174 | // write a function that returns an integer value from an interface{}
175 | func TestIntFromInterface(t *testing.T) {
176 | defer testPanic(t) // handle panics and syntax errors
177 | setTestInstance(t) // update test instance for logging
178 |
179 | t.Log("GOAL: you shoud be able to get a value from an interface")
180 | var x interface{}
181 | var i int
182 | var r int
183 |
184 | x = 5
185 | r = 5
186 | i = intFromInterface(x)
187 | if i != r {
188 | t.Error(shouldBe(i, r))
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/app/regex_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import "testing"
4 |
5 | // write a function that checks if the string contains a decimal number
6 | func TestContainsNumber(t *testing.T) {
7 | defer testPanic(t) // handle panics and syntax errors
8 | setTestInstance(t) // update test instance for logging
9 |
10 | t.Log("GOAL: you should be able to detect a number in a string")
11 | var b bool
12 |
13 | b = containsNumber("abc123")
14 | if !b {
15 | t.Error(shouldBe(b, true))
16 | }
17 |
18 | b = containsNumber("abc")
19 | if b {
20 | t.Error(shouldBe(b, false))
21 | }
22 | }
23 |
24 | // write a function that checks if the string ends with a vowel
25 | func TestEndsWithVowel(t *testing.T) {
26 | defer testPanic(t) // handle panics and syntax errors
27 | setTestInstance(t) // update test instance for logging
28 |
29 | t.Log("GOAL: you should be able to determine whether a string ends with a vowel (aeiou)")
30 | var b bool
31 |
32 | b = endsWithVowel("cats")
33 | if b {
34 | t.Error(shouldBe(b, false))
35 | }
36 |
37 | b = endsWithVowel("gorilla")
38 | if !b {
39 | t.Error(shouldBe(b, true))
40 | }
41 |
42 | b = endsWithVowel("I KNOW KUNG FU")
43 | if !b {
44 | t.Error(shouldBe(b, true))
45 | }
46 | }
47 |
48 | // write a function that captures and returns the first string of 3 decimal digits
49 | func TestCaptureThreeNumbers(t *testing.T) {
50 | defer testPanic(t) // handle panics and syntax errors
51 | setTestInstance(t) // update test instance for logging
52 |
53 | t.Log("GOAL: you should be able to capture the first series of three numbers")
54 | var s string
55 | var r string
56 |
57 | s = "abc123"
58 | r = "123"
59 | s = captureThreeNumbers(s)
60 | if s != r {
61 | t.Error(shouldBe(s, r))
62 | }
63 |
64 | s = "9876543"
65 | r = "987"
66 | s = captureThreeNumbers(s)
67 | if s != r {
68 | t.Error(shouldBe(s, r))
69 | }
70 |
71 | s = "abcdef"
72 | r = ""
73 | s = captureThreeNumbers(s)
74 | if s != r {
75 | t.Error(shouldBe(s, r))
76 | }
77 |
78 | s = "12ab12ab"
79 | r = ""
80 | s = captureThreeNumbers(s)
81 | if s != r {
82 | t.Error(shouldBe(s, r))
83 | }
84 | }
85 |
86 | // write a function that checks if the string matches a specified pattern
87 | func TestMatchesPattern(t *testing.T) {
88 | defer testPanic(t) // handle panics and syntax errors
89 | setTestInstance(t) // update test instance for logging
90 |
91 | t.Log("GOAL: you should be able to determine whether a string matches a pattern")
92 | // the pattern is XXX-XXX-XXXX where all X"s are digits
93 | var b bool
94 |
95 | b = matchesPattern("800-555-1212")
96 | if !b {
97 | t.Error(shouldBe(b, true))
98 | }
99 |
100 | b = matchesPattern("451-933-7899")
101 | if !b {
102 | t.Error(shouldBe(b, true))
103 | }
104 |
105 | b = matchesPattern("33-444-5555")
106 | if b {
107 | t.Error(shouldBe(b, false))
108 | }
109 |
110 | b = matchesPattern("abc-def-hijk")
111 | if b {
112 | t.Error(shouldBe(b, false))
113 | }
114 |
115 | b = matchesPattern("1800-555-1212")
116 | if b {
117 | t.Error(shouldBe(b, false))
118 | }
119 |
120 | b = matchesPattern("800-555-12121")
121 | if b {
122 | t.Error(shouldBe(b, false))
123 | }
124 |
125 | b = matchesPattern("800-5555-1212")
126 | if b {
127 | t.Error(shouldBe(b, false))
128 | }
129 |
130 | b = matchesPattern("800-55-1212")
131 | if b {
132 | t.Error(shouldBe(b, false))
133 | }
134 | }
135 |
136 | // write a function that checks if the string is a correctly-formatted monetary amounts in USD
137 | func TestIsUSD(t *testing.T) {
138 | defer testPanic(t) // handle panics and syntax errors
139 | setTestInstance(t) // update test instance for logging
140 |
141 | t.Log("GOAL: you should be able to detect correctly-formatted monetary amounts in USD")
142 | var b bool
143 | // TRUE
144 |
145 | b = isUSD("$1")
146 | if !b {
147 | t.Error(shouldBe(b, true))
148 | }
149 |
150 | b = isUSD("$12")
151 | if !b {
152 | t.Error(shouldBe(b, true))
153 | }
154 |
155 | b = isUSD("$123")
156 | if !b {
157 | t.Error(shouldBe(b, true))
158 | }
159 |
160 | b = isUSD("$1234")
161 | if b {
162 | t.Error(shouldBe(b, false))
163 | }
164 |
165 | b = isUSD("$132.03")
166 | if !b {
167 | t.Error(shouldBe(b, true))
168 | }
169 |
170 | b = isUSD("$32.03")
171 | if !b {
172 | t.Error(shouldBe(b, true))
173 | }
174 |
175 | b = isUSD("$2.03")
176 | if !b {
177 | t.Error(shouldBe(b, true))
178 | }
179 |
180 | b = isUSD("$1,023,032.03")
181 | if !b {
182 | t.Error(shouldBe(b, true))
183 | }
184 |
185 | b = isUSD("$20,933,209.93")
186 | if !b {
187 | t.Error(shouldBe(b, true))
188 | }
189 |
190 | b = isUSD("$20,933,209")
191 | if !b {
192 | t.Error(shouldBe(b, true))
193 | }
194 |
195 | b = isUSD("$459,049,393.21")
196 | if !b {
197 | t.Error(shouldBe(b, true))
198 | }
199 |
200 | // FALSE
201 | b = isUSD("34,344.34")
202 | if b {
203 | t.Error(shouldBe(b, false))
204 | }
205 |
206 | b = isUSD("$,344.34")
207 | if b {
208 | t.Error(shouldBe(b, false))
209 | }
210 |
211 | if b {
212 | t.Error(shouldBe(b, false))
213 | }
214 |
215 | if b {
216 | t.Error(shouldBe(b, false))
217 | }
218 |
219 | b = isUSD("$34,344.3")
220 | if b {
221 | t.Error(shouldBe(b, false))
222 | }
223 |
224 | b = isUSD("$34,344.344")
225 | if b {
226 | t.Error(shouldBe(b, false))
227 | }
228 |
229 | b = isUSD("$34,344_34")
230 | if b {
231 | t.Error(shouldBe(b, false))
232 | }
233 |
234 | b = isUSD("$3,432,12.12")
235 | if b {
236 | t.Error(shouldBe(b, false))
237 | }
238 |
239 | b = isUSD("$3,432,1,034.12")
240 | if b {
241 | t.Error(shouldBe(b, false))
242 | }
243 |
244 | b = isUSD("4$3,432,034.12")
245 | if b {
246 | t.Error(shouldBe(b, false))
247 | }
248 |
249 | b = isUSD("$2.03.45")
250 | if b {
251 | t.Error(shouldBe(b, false))
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/app/strings_test.go:
--------------------------------------------------------------------------------
1 | package goassessment
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | // write a function that composes a string from arguments
8 | func TestStringCompose(t *testing.T) {
9 | defer testPanic(t) // handle panics and syntax errors
10 | setTestInstance(t) // update test instance for logging
11 |
12 | t.Log("GOAL: you should be able to compose a string from arguments")
13 | var s string
14 | var r string
15 | s = composeString("Hello", "World")
16 | r = "Hello, World\n"
17 |
18 | if s != r {
19 | t.Error(shouldBe(s, r))
20 | }
21 | }
22 |
23 | // write a function that returns a string from a byte array
24 | func TestStringFromBytes(t *testing.T) {
25 | defer testPanic(t) // handle panics and syntax errors
26 | setTestInstance(t) // update test instance for logging
27 |
28 | t.Log("GOAL: you should be able to convert a byte array to a string")
29 |
30 | b := []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 10}
31 | s := fromBytes(b)
32 | r := "Hello, World\n"
33 |
34 | if s != r {
35 | t.Error(shouldBe(s, r))
36 | }
37 | }
38 |
39 | // write a function that takes a string and returns an array of runes
40 | func TestRunes(t *testing.T) {
41 | defer testPanic(t) // handle panics and syntax errors
42 | setTestInstance(t) // update test instance for logging
43 |
44 | t.Log("GOAL: you should be able to decompose a UTF-8 string into an array of runes")
45 | var s string
46 | var r []rune
47 | var u []rune
48 |
49 | s = "✋ 👍 👎 ✊"
50 | u = []rune{9995, 32, 128077, 32, 128078, 32, 9994}
51 | r = runesFromString(s)
52 |
53 | // are they the same length
54 | if len(u) != len(r) {
55 | t.Error(shouldBe(len(u), len(r)))
56 | return
57 | }
58 |
59 | // are they equal
60 | b := true
61 | for i := 0; i < len(u); i++ {
62 | if r[i] != u[i] {
63 | b = false
64 | break
65 | }
66 | }
67 |
68 | if !b {
69 | t.Error(shouldBe(r, u))
70 | }
71 | }
72 |
73 | // write a function that splits a string into words
74 | func TestSplitString(t *testing.T) {
75 | defer testPanic(t) // handle panics and syntax errors
76 | setTestInstance(t) // update test instance for logging
77 |
78 | t.Log("GOAL: you should be able to split a string into words")
79 | var s string
80 | var r []string
81 | var u []string
82 | s = "Hello, World"
83 | u = []string{"Hello", "World"}
84 |
85 | r = splitString(s)
86 |
87 | // check length equal
88 | if len(r) != len(u) {
89 | t.Error(shouldBe(len(r), len(u)))
90 | return
91 | }
92 |
93 | // check words
94 | for i := 0; i < len(u); i++ {
95 | if u[i] != r[i] {
96 | t.Error(shouldBe(u, r))
97 | }
98 | }
99 | }
100 |
101 | // write a function that converts a string to Title Case
102 | func TestTitleCase(t *testing.T) {
103 | defer testPanic(t) // handle panics and syntax errors
104 | setTestInstance(t) // update test instance for logging
105 |
106 | t.Log("GOAL: you should be able to convert a string to title case")
107 | var s string
108 | var r string
109 | var u string
110 | s = "this is the title"
111 | u = "This Is The Title"
112 |
113 | r = titleString(s)
114 |
115 | // check length equal
116 | if r != u {
117 | t.Error(shouldBe(r, u))
118 | }
119 | }
120 |
121 | // write a function that reduces repeated characters
122 | func TestReduceString(t *testing.T) {
123 | defer testPanic(t) // handle panics and syntax errors
124 | setTestInstance(t) // update test instance for logging
125 |
126 | t.Log("GOAL: you should be able to reduce adjacent repeated characters to a desired minimum")
127 | var s string
128 | var r string
129 |
130 | s = "aaaabbbb"
131 | r = "aabb"
132 | s = reduceString(s, 2)
133 | if s != r {
134 | t.Error(shouldBe(s, r))
135 | }
136 |
137 | s = "xaaabbbb"
138 | r = "xaabb"
139 | s = reduceString(s, 2)
140 | if s != r {
141 | t.Error(shouldBe(s, r))
142 | }
143 |
144 | s = "aaaabbbb"
145 | r = "ab"
146 | s = reduceString(s, 1)
147 | if s != r {
148 | t.Error(shouldBe(s, r))
149 | }
150 |
151 | s = "aaxxxaabbbb"
152 | r = "aaxxaabb"
153 | s = reduceString(s, 2)
154 | if s != r {
155 | t.Error(shouldBe(s, r))
156 | }
157 |
158 | s = "aaxxxaabbbb"
159 | r = "axab"
160 | s = reduceString(s, 1)
161 | if s != r {
162 | t.Error(shouldBe(s, r))
163 | }
164 | }
165 |
166 | // write a function that wraps lines at a given number of columns without breaking works
167 | func TestWordWrap(t *testing.T) {
168 | defer testPanic(t) // handle panics and syntax errors
169 | setTestInstance(t) // update test instance for logging
170 |
171 | t.Log("GOAL: you should be able to wrap lines at a given number of columns, without breaking words")
172 | // !!! use underscore to indicate where a wrap would occur
173 |
174 | column := 5
175 | s := "abcdef abcde abc def"
176 | r := "abcdef_abcde_abc_def"
177 | s = wordWrap(s, column)
178 | if s != r {
179 | t.Error(shouldBe(s, r))
180 | }
181 |
182 | s = "abc abc abc"
183 | r = "abc_abc_abc"
184 | s = wordWrap(s, column)
185 | if s != r {
186 | t.Error(shouldBe(s, r))
187 | }
188 | s = "a b c def"
189 | r = "a b c_def"
190 |
191 | s = wordWrap(s, column)
192 | if s != r {
193 | t.Error(shouldBe(s, r))
194 | }
195 |
196 | }
197 |
198 | // write a function that reverses the characters in a string
199 | func TestReverseString(t *testing.T) {
200 | defer testPanic(t) // handle panics and syntax errors
201 | setTestInstance(t) // update test instance for logging
202 |
203 | t.Log("GOAL: you should be able to reverse the characters in a string")
204 | var s string
205 | var r string
206 |
207 | s = "abc"
208 | r = "cba"
209 | s = reverseString(s)
210 | if s != r {
211 | t.Error(shouldBe(s, r))
212 | }
213 |
214 | s = "i am a string of characters"
215 | r = "sretcarahc fo gnirts a ma i"
216 | s = reverseString(s)
217 | if s != r {
218 | t.Error(shouldBe(s, r))
219 | }
220 |
221 | }
222 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-assessment
2 |
3 |
4 |
5 |
6 |
7 | **Your Job Is To Make The Tests Pass!**
8 |
9 | ### What is this?
10 |
11 | This is a tool for assessing or practicing beginner level programming in Golang. It is [inspired by Rebecca Murphey's js-assessment](https://github.com/rmurphey/js-assessment)
12 |
13 | A while back I tried out Murphey's js-assessment and found it to be a valuable tool for refreshing my javascript capability when I
14 | had not written any JavaScript for a few months. I figured a similar tool for Go might be useful
15 | for the same reason.
16 |
17 | This tool leads the user through a series of Go implementation exercises from easy to medium.
18 | It's not meant to be a leetcode challenge. The point is to exercise your skill with some basic syntax and semantics of Go.
19 | Most of the tests and implementations are straightforward. A few are harder, require a bit of
20 | algorithm knowledge or the ability to look up a solution. If you have gone through the
21 | official golang tutorial you will know enough to make most of the tests pass. For the rest
22 | go ahead and look up a solution if you need to. Feel free to use go standard libraries.
23 | No need to reinvent the wheel.
24 |
25 | This tool relies on Test Driven Development using the
26 | [native Go test framework](https://golang.org/pkg/testing/). The ./app directory
27 | contains a set of test files (app/_topic_\_test.go) and a corresponding app skeleton file (app/_topic_.go) with
28 | the required function definitions but missing the implementations. Treat the _topic_\_test.go files
29 | as the specifications for the function skeletons. The goal is to provide the implementations in the
30 | skeleton files to pass the tests. If the tests are not passing, dig into the _topic_\_test.go files
31 | to figure out what the requirement is, then fix the skeleton file so the tests pass.
32 | You are done when all tests pass. You should not have to modify anything in the _topic_\_test.go files,
33 | only the _topic_.go skeleton files (unless the tests have a bug in which case feel free to report it).
34 |
35 | - if you are stuck, don't hesitate to get help. Look it up!
36 | - if you have a compile error in a function under test (inside one of the _topic_.go files) it will terminate the tests for that topic. You will (probably) get an error message describing where and what the compile error is.
37 |
38 | The tests are ordered (more or less) by increasing difficulty. The topics covered include
39 |
40 | - Operators
41 | - Flow Control
42 | - Strings
43 | - Slices
44 | - Binary
45 | - Functions
46 | - Recursion
47 | - Methods
48 | - Collections
49 | - Regexp
50 | - Async
51 |
52 | Here's how to work this:
53 |
54 | 1. Set up Go
55 | 2. Clone or fork this repo
56 | 3. Start the browser based test framework (test_browser.sh or test_browser.cmd). Open a browser to the host:port (:8080 by default)
57 | 4. Refresh the page to run the tests
58 | 5. Update the skeleton files in ./app
59 | 6. When all tests pass you are done
60 |
61 | #### Running the Tests
62 |
63 | To run the tests and see their results you have some choices.
64 |
65 | 1. From a web browser : **best**
66 | - the tool includes a web server implementation that will run the tests and display the results for you.
67 | - in the root of the repo, run './test_browser.sh' (or 'test_browser.cmd' on Windows. see the issue regarding Windows Antivirus if you have problems) and connect to the hostip:8080 with a browser. You will get a display of all the test results.
68 | - after editing skeleton files, you can update the test results by refreshing the web page. this will rerun the tests and update the results.
69 | - you don't need to restart the web server when you change a function under test. just refresh the page. If you modify a _topic_\_test.go file, you would need to restart the server.
70 | - the browser tool needs to be connected to the internet because it uses Bootstrap from a CDN. If you want to run locally you can download the required bootstrap files and modify './static/index.html' to point to their local copies.
71 | - You can change the port number in './main.go' or in the startup scripts.
72 | 2. From the Shell
73 | - run a shell script that runs all the tests and writes the output to 'results.txt'
74 | - for Linux or Mac, the script is './test_all.sh'
75 | - for Windows, the script is 'test_all.cmd'
76 | - if your tests and other go programs build slowly on Windows, its probably due to Windows Defender or other antivirus
77 | - you can run individual test files using 'go test...'
78 | - example for the strings test
79 | - \[linux/macos\]: 'go test -v ./app/strings\*.go ./app/test_util.go ./app/app_types.go'
80 | - \[windows\]: 'go test -v ./app/strings.go ./app/strings_test.go ./app/test_util.go ./app/app_types.go'
81 |
82 | #### Debugging the functions under test
83 |
84 | 1. Visual Studio Code (Or other debugger with similar capability)
85 |
86 | Visual Studio Code set up for Go programming provides the capability to run individual tests and use breakpoints and other debugger functions.
87 |
88 | With this setup, you can run individual tests within the _topic_\_test.go functions. VS Code will superimpose buttons
89 | at the top of each test to let you run or debug the test function. This allows you to step into the actual function
90 | under test and observe it. You should never have to change anything in the _topic_\_test.go function itself.
91 |
92 | This is independent of whether or not the browser test setup is running. You can do both at the same time. If you
93 | are viewing the browser based test output while editing with VS Code and you need to debug a specific test, you can
94 | use the VS Code test debug function and still update results with a browser refresh
95 |
96 | 2. Logging output in a function under test
97 |
98 | If you want to debug using printouts from within a function under test, the test*util.go file provides the \_testLog(s string)* function.
99 | You can print output using _testLog_ from within a function in an app skeleton file. Due to the way the golang testing system works, the output from these logs may come after the results for that test are printed. The log output specifies the file and line number of where the testLog was called.
100 |
101 | #### Go Modules
102 |
103 | This code is set up to be using Modules rather than GOPATH. As it is, it does not import or use any modules besides standard Go libraries. You could probably fix it to use GOPATH if you require that.
104 |
--------------------------------------------------------------------------------
/parse.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "regexp"
6 | "strings"
7 | )
8 |
9 | // results of a single test
10 | type testResults struct {
11 | pass int
12 | fail int
13 | html []string
14 | }
15 |
16 | func parseTestLine(line string, ti *testResults) {
17 | var matched bool
18 | var err error
19 | var r *regexp.Regexp
20 | var s []string
21 |
22 | // ===================================================================
23 | // find matches to the line format and output the corresponding HTML
24 | // ===================================================================
25 |
26 | // @start
27 | r, err = regexp.Compile(`@start (.+)`)
28 | s = r.FindStringSubmatch(line)
29 | if err != nil {
30 | panic(err)
31 | }
32 | if len(s) >= 2 {
33 | h := []string{
34 | `