Skip to content

Commit

Permalink
Add weather forecast golang exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
anaschwendler committed Oct 10, 2024
1 parent a1fb018 commit c0153ad
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 0 deletions.
41 changes: 41 additions & 0 deletions go/weather-forecast/HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Help

## Running the tests

To run the tests run the command `go test` from within the exercise directory.

If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
flags:

go test -v --bench . --benchmem

Keep in mind that each reviewer will run benchmarks on a different machine, with
different specs, so the results from these benchmark tests may vary.

## Submitting your solution

You can submit your solution using the `exercism submit weather_forecast.go` command.
This command will upload your solution to the Exercism website and print the solution page's URL.

It's possible to submit an incomplete solution which allows you to:

- See how others have completed the exercise
- Request help from a mentor

## Need to get help?

If you'd like help solving the exercise, check the following pages:

- The [Go track's documentation](https://exercism.org/docs/tracks/go)
- The [Go track's programming category on the forum](https://forum.exercism.org/c/programming/go)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)

Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.

To get help if you're having trouble, you can use one of the following resources:

- [How to Write Go Code](https://golang.org/doc/code.html)
- [Effective Go](https://golang.org/doc/effective_go.html)
- [Go Resources](http://golang.org/help)
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)
20 changes: 20 additions & 0 deletions go/weather-forecast/HINTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Hints

## General

- A [documentation comment][comment] should be written directly before the entity that it is describing, start with the name of what it is describing, take the form of a sentence, and end with a period.

## 1. Document package weather

- The [package comment][comment] should be written directly before the package, start with `Package x`, and end with a period.

## 2. Document the CurrentCondition and CurrentLocation variables

- The [variable comment][variable comment] should be written right before the variable that it is describing, start with its name, and end with a period.

## 3. Document the Forecast() function

- The [function comment][comment] should come directly before the function, start with the name of the function and end with a period.

[comment]: https://golang.org/doc/effective_go.html#commentary
[variable comment]: https://dave.cheney.net/practical-go/presentations/qcon-china.html#_comments
66 changes: 66 additions & 0 deletions go/weather-forecast/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Weather Forecast

Welcome to Weather Forecast on Exercism's Go Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)

## Introduction

In the previous exercise, we saw that there are two ways to write comments in Go: single-line comments that are preceded by `//`, and multiline comment blocks that are wrapped with `/*` and `*/`.

## Documentation comments

In Go, comments play an important role in documenting code. They are used by the `godoc` command, which extracts these comments to create documentation about Go packages. A documentation comment should be a complete sentence that starts with the name of the thing being described and ends with a period.

Comments should precede packages as well as exported identifiers, for example exported functions, methods, package variables, constants, and structs, which you will learn more about in the next exercises.

A package-level variable can look like this:

```go
// TemperatureCelsius represents a certain temperature in degrees Celsius.
var TemperatureCelsius float64
```

## Package comments

Package comments should be written directly before a package clause (`package x`) and begin with `Package x ...` like this:

```go
// Package kelvin provides tools to convert
// temperatures to and from Kelvin.
package kelvin
```

## Function comments

A function comment should be written directly before the function declaration. It should be a full sentence that starts with the function name. For example, an exported comment for the function `Calculate` should take the form `Calculate ...`. It should also explain what arguments the function takes, what it does with them, and what its return values mean, ending in a period):

```go
// CelsiusFreezingTemp returns an integer value equal to the temperature at which water freezes in degrees Celsius.
func CelsiusFreezingTemp() int {
return 0
}
```

## Instructions

Goblinocus is a country that takes its weather forecast very seriously. Since you are a renowned, responsible and proficient developer, they asked you to write a program that can forecast the current weather condition of various cities in Goblinocus. You were busy at the time and asked one of your friends to do the job instead. After a while, the president of Goblinocus contacted you and said they do not understand your friend's code. When you check the code, you discover that your friend did not act as a responsible programmer and there are no comments in the code. You feel obligated to clarify the program so goblins can understand them as well.

## 1. Document package weather

Since goblins are not as smart as you are, they forgot what the package should do for them. Please write a comment for `package weather` that describes its contents. The package comment should introduce the package and provide information relevant to the package as a whole.

## 2. Document the CurrentCondition and CurrentLocation variables

The president of Goblinocus is a bit paranoid and fears uncommented variables are used to destroy their country. Please clarify the usage of the package variables `CurrentCondition` and `CurrentLocation` and put the president's mind at ease. This should tell any user of the package what information the variables store, and what they can do with it.

## 3. Document the Forecast() function

Goblinocus forecast operators want to know what the `Forecast()` function does (but do not tell them how it works, since unfortunately, they will get more confused). Please write a comment for this function that describes what the function does, but not how it does it.

## Source

### Created by

- @nikimanoledaki
- @micuffaro
3 changes: 3 additions & 0 deletions go/weather-forecast/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module weather

go 1.18
13 changes: 13 additions & 0 deletions go/weather-forecast/weather_forecast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Package weather_forecast provides a function to get the current weather condition for a given city.
package weather

// CurrentCondition returns the current weather condition for a given city.
var CurrentCondition string
// CurrentLocation is the city for which the weather condition is being reported.
var CurrentLocation string

// Forecast returns the current weather condition for a given city.
func Forecast(city, condition string) string {
CurrentLocation, CurrentCondition = city, condition
return CurrentLocation + " - current weather condition: " + CurrentCondition
}
140 changes: 140 additions & 0 deletions go/weather-forecast/weather_forecast_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package weather

import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"strings"
"testing"
)

func TestComments(t *testing.T) {
filename := "weather_forecast.go"

fs := token.NewFileSet()
f, err := parser.ParseFile(fs, filename, nil, parser.ParseComments)
if err != nil {
t.Fatal(err)
}

wantedComments := 4
got := len(f.Comments)
if got != wantedComments {
t.Errorf("Incorrect number of comments: got %d, want %d", got, wantedComments)
}

testPackageComment(t, f)

ast.Inspect(f, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.GenDecl:
if n.Lparen.IsValid() {
for _, v := range n.Specs {
testBlockIdentifierComment(t, v.(*ast.ValueSpec))
}
} else {
testIdentifierComment(t, n)
}
case *ast.FuncDecl:
testFunctionComment(t, n)
}
return true
})
}

func testPackageComment(t *testing.T, node *ast.File) {
t.Helper()
if node.Doc == nil {
t.Errorf("Package weather should have a comment")
}

packageName := node.Name.Name
want := "Package " + packageName
packageComment := node.Doc.Text()

if ok, errStr := testComment("Package", packageName, packageComment, want); !ok {
t.Error(errStr)
}

}
func testIdentifierComment(t *testing.T, node *ast.GenDecl) {
t.Helper()

identifierName := node.Specs[0].(*ast.ValueSpec).Names[0].Name
if node.Doc == nil {
t.Errorf("Exported identifier %s should have a comment", identifierName)
}

identifierComment := node.Doc.Text()
want := identifierName

if ok, errStr := testComment("Variable", identifierName, identifierComment, want); !ok {
t.Error(errStr)
}
}

func testBlockIdentifierComment(t *testing.T, node *ast.ValueSpec) {
t.Helper()

identifierName := node.Names[0].Name
if node.Doc == nil {
t.Errorf("Exported identifier %s should have a comment", identifierName)
}

identifierComment := node.Doc.Text()
want := identifierName

if ok, errStr := testComment("Variable", identifierName, identifierComment, want); !ok {
t.Error(errStr)
}

}

func testFunctionComment(t *testing.T, node *ast.FuncDecl) {
t.Helper()
funcName := node.Name.Name
if node.Doc == nil {
t.Errorf("Exported function %s() should have a comment", funcName)
}

funcComment := node.Doc.Text()
want := funcName

if ok, errStr := testComment("Function", funcName, funcComment, want); !ok {
t.Error(errStr)
}
}

func testComment(entityKind, entityName, comment, wantedPrefix string) (ok bool, errString string) {

trimmedComment := strings.TrimSpace(comment)
lowerEntity := strings.ToLower(entityKind)

// Check if comment has wanted prefix
if !strings.HasPrefix(trimmedComment, wantedPrefix) {
errorString := fmt.Sprintf("%s comment for %s '%s' should start with '// %s ...': got '// %s'",
entityKind, lowerEntity, entityName, wantedPrefix, trimmedComment)
return false, errorString
}

// Check if comment content is empty
commentContent := strings.TrimPrefix(trimmedComment, wantedPrefix)
commentContent = strings.TrimSpace(commentContent)
commentContent = strings.TrimSuffix(commentContent, ".")

if commentContent == "" {
lowerEntity := strings.ToLower(entityKind)
errorString := fmt.Sprintf("%s comment of '%s' should provide a description of the %s, e.g '// %s <%s_description>'",
entityKind, entityName, lowerEntity, wantedPrefix, lowerEntity)
return false, errorString
}

// Check if comment ends in a period
if !strings.HasSuffix(trimmedComment, ".") {
return false, fmt.Sprintf("%s comment for %s '%s' should end with a period (.)",
entityKind, lowerEntity, entityName)
}

return true, ""
}

0 comments on commit c0153ad

Please sign in to comment.