mirror of
https://codeberg.org/scip/rpnc.git
synced 2025-12-16 12:01:01 +01:00
Internal/add gh actions and tests (#3)
* add gh actions and templates * add show-versions in Makefile * force go 1.20 * added test facilities
This commit is contained in:
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[bug-report]"
|
||||
labels: bug
|
||||
assignees: TLINDEN
|
||||
|
||||
---
|
||||
|
||||
**Describtion**
|
||||
<!-- Please provide a clear and concise description of the issue: -->
|
||||
|
||||
|
||||
**Steps To Reproduce**
|
||||
<!-- Please detail the steps to reproduce the behavior: -->
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
<!-- What do you expected to happen instead? -->
|
||||
|
||||
|
||||
**Version information**
|
||||
<!--
|
||||
Please provide as much version information as possible:
|
||||
- if you have just installed a binary, provide the output of: rpn -v
|
||||
- if you installed from source, provide the output of: make show-version
|
||||
- provide additional details: operating system and version and shell environment
|
||||
-->
|
||||
|
||||
|
||||
**Additional informations**
|
||||
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a feature
|
||||
title: "[feature-request]"
|
||||
labels: feature-request
|
||||
assignees: TLINDEN
|
||||
|
||||
---
|
||||
|
||||
**Describtion**
|
||||
<!-- Please provide a clear and concise description of the feature you desire: -->
|
||||
|
||||
|
||||
|
||||
**Version information**
|
||||
<!--
|
||||
Just in case the feature is already present, please provide as
|
||||
much version information as possible:
|
||||
- if you have just installed a binary, provide the output of: rpn -v
|
||||
- if you installed from source, provide the output of: make show-version
|
||||
- provide additional details: operating system and version and shell environment
|
||||
-->
|
||||
|
||||
36
.github/workflows/ci.yaml
vendored
Normal file
36
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: build-and-test-rpn
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
version: [1.21]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
name: Build
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ matrix.version }}
|
||||
id: go
|
||||
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: build
|
||||
run: go build
|
||||
|
||||
- name: test
|
||||
run: make test
|
||||
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
- uses: actions/checkout@v3
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
releases
|
||||
rpn
|
||||
rpn.1
|
||||
coverage.out
|
||||
|
||||
17
Makefile
17
Makefile
@@ -39,10 +39,6 @@ ifdef HAVE_POD
|
||||
echo "var manpage = \`" >> $*.go
|
||||
pod2text $*.pod >> $*.go
|
||||
echo "\`" >> $*.go
|
||||
|
||||
echo "var usage = \`" >> $*.go
|
||||
awk '/SYNOPS/{f=1;next} /DESCR/{f=0} f' $*.pod | sed 's/^ //' >> $*.go
|
||||
echo "\`" >> $*.go
|
||||
endif
|
||||
|
||||
buildlocal:
|
||||
@@ -59,7 +55,6 @@ clean:
|
||||
|
||||
test:
|
||||
go test -v ./...
|
||||
bash t/test.sh
|
||||
|
||||
singletest:
|
||||
@echo "Call like this: ''make singletest TEST=TestPrepareColumns MOD=lib"
|
||||
@@ -77,3 +72,15 @@ buildall:
|
||||
|
||||
release: buildall
|
||||
gh release create v$(VERSION) --generate-notes releases/*
|
||||
|
||||
show-versions: buildlocal
|
||||
@echo "### rpn version:"
|
||||
@./rpn -v
|
||||
|
||||
@echo
|
||||
@echo "### go module versions:"
|
||||
@go list -m all
|
||||
|
||||
@echo
|
||||
@echo "### go version used for building:"
|
||||
@grep -m 1 go go.mod
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
[](https://github.com/tlinden/rpnc/actions)
|
||||
[](https://github.com/tlinden/rpnc/blob/master/LICENSE)
|
||||
[](https://goreportcard.com/report/github.com/tlinden/rpnc)
|
||||
|
||||
## Reverse Polish Notation Calculator for the commandline
|
||||
|
||||
This is a small commandline calculator which takes its input in
|
||||
|
||||
45
calc.go
45
calc.go
@@ -20,7 +20,6 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -376,50 +375,6 @@ func (c *Calc) Debug(msg string) {
|
||||
}
|
||||
}
|
||||
|
||||
// do simple calculations
|
||||
func (c *Calc) simple(op byte) {
|
||||
c.stack.Backup()
|
||||
|
||||
for c.stack.Len() > 1 {
|
||||
b := c.stack.Pop()
|
||||
a := c.stack.Pop()
|
||||
var x float64
|
||||
|
||||
c.Debug(fmt.Sprintf("evaluating: %.2f %c %.2f", a, op, b))
|
||||
|
||||
switch op {
|
||||
case '+':
|
||||
x = a + b
|
||||
case '-':
|
||||
x = a - b
|
||||
case 'x':
|
||||
fallthrough // alias for *
|
||||
case '*':
|
||||
x = a * b
|
||||
case '/':
|
||||
if b == 0 {
|
||||
fmt.Println("error: division by null!")
|
||||
return
|
||||
}
|
||||
x = a / b
|
||||
case '^':
|
||||
x = math.Pow(a, b)
|
||||
default:
|
||||
panic("invalid operator!")
|
||||
}
|
||||
|
||||
c.stack.Push(x)
|
||||
|
||||
c.History("%f %c %f = %f", a, op, b, x)
|
||||
|
||||
if !c.batch {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.Result()
|
||||
}
|
||||
|
||||
func (c *Calc) luafunc(funcname string) {
|
||||
// called from calc loop
|
||||
var x float64
|
||||
|
||||
141
calc_test.go
Normal file
141
calc_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright © 2023 Thomas von Dein
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCalc(t *testing.T) {
|
||||
calc := NewCalc()
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
cmd string
|
||||
exp float64
|
||||
batch bool
|
||||
}{
|
||||
{
|
||||
name: "plus",
|
||||
cmd: `15 15 +`,
|
||||
exp: 30,
|
||||
},
|
||||
{
|
||||
name: "power",
|
||||
cmd: `4 2 ^`,
|
||||
exp: 16,
|
||||
},
|
||||
{
|
||||
name: "minus",
|
||||
cmd: `100 50 -`,
|
||||
exp: 50,
|
||||
},
|
||||
{
|
||||
name: "multi",
|
||||
cmd: `4 4 x`,
|
||||
exp: 16,
|
||||
},
|
||||
{
|
||||
name: "divide",
|
||||
cmd: `10 2 /`,
|
||||
exp: 5,
|
||||
},
|
||||
{
|
||||
name: "percent",
|
||||
cmd: `400 20 %`,
|
||||
exp: 80,
|
||||
},
|
||||
{
|
||||
name: "percent-minus",
|
||||
cmd: `400 20 %-`,
|
||||
exp: 320,
|
||||
},
|
||||
{
|
||||
name: "percent-plus",
|
||||
cmd: `400 20 %+`,
|
||||
exp: 480,
|
||||
},
|
||||
{
|
||||
name: "mod",
|
||||
cmd: `9 2 mod`,
|
||||
exp: 1,
|
||||
},
|
||||
{
|
||||
name: "sqrt",
|
||||
cmd: `16 sqrt`,
|
||||
exp: 4,
|
||||
},
|
||||
{
|
||||
name: "ceil",
|
||||
cmd: `15.5 ceil`,
|
||||
exp: 16,
|
||||
},
|
||||
{
|
||||
name: "dim",
|
||||
cmd: `6 4 dim`,
|
||||
exp: 2,
|
||||
},
|
||||
{
|
||||
name: "batch-sum",
|
||||
cmd: `2 2 2 2 sum`,
|
||||
exp: 8,
|
||||
batch: true,
|
||||
},
|
||||
{
|
||||
name: "batch-median",
|
||||
cmd: `1 2 3 4 5 median`,
|
||||
exp: 3,
|
||||
batch: true,
|
||||
},
|
||||
{
|
||||
name: "batch-mean",
|
||||
cmd: `2 2 8 2 2 mean`,
|
||||
exp: 3.2,
|
||||
batch: true,
|
||||
},
|
||||
{
|
||||
name: "batch-min",
|
||||
cmd: `1 2 3 4 5 min`,
|
||||
exp: 1,
|
||||
batch: true,
|
||||
},
|
||||
{
|
||||
name: "batch-max",
|
||||
cmd: `1 2 3 4 5 max`,
|
||||
exp: 5,
|
||||
batch: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
testname := fmt.Sprintf("cmd-%s-expect-%.2f",
|
||||
tt.name, tt.exp)
|
||||
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
calc.batch = tt.batch
|
||||
calc.Eval(tt.cmd)
|
||||
got := calc.Result()
|
||||
calc.stack.Clear()
|
||||
if got != tt.exp {
|
||||
t.Errorf("calc failed:\n+++ got: %f\n--- want: %f",
|
||||
got, tt.exp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
14
rpn.go
14
rpn.go
@@ -222,17 +222,3 @@ AUTHORS
|
||||
Thomas von Dein tom AT vondein DOT org
|
||||
|
||||
`
|
||||
var usage = `
|
||||
|
||||
Usage: rpn [-bdvh] [<operator>]
|
||||
|
||||
Options:
|
||||
-b, --batchmode enable batch mode
|
||||
-d, --debug enable debug mode
|
||||
-v, --version show version
|
||||
-h, --help show help
|
||||
|
||||
When <operator> is given, batch mode ist automatically enabled. Use
|
||||
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
|
||||
|
||||
`
|
||||
|
||||
190
stack_test.go
Normal file
190
stack_test.go
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright © 2023 Thomas von Dein
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPush(t *testing.T) {
|
||||
t.Run("push", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
s.Push(5)
|
||||
|
||||
if s.linklist.Back().Value != 5.0 {
|
||||
t.Errorf("push failed:\n+++ got: %f\n--- want: %f",
|
||||
s.linklist.Back().Value, 5.0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPop(t *testing.T) {
|
||||
t.Run("pop", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
s.Push(5)
|
||||
got := s.Pop()
|
||||
|
||||
if got != 5.0 {
|
||||
t.Errorf("pop failed:\n+++ got: %f\n--- want: %f",
|
||||
got, 5.0)
|
||||
}
|
||||
|
||||
if s.Len() != 0 {
|
||||
t.Errorf("stack not empty after pop()")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPops(t *testing.T) {
|
||||
t.Run("pops", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
s.Push(5)
|
||||
s.Push(5)
|
||||
s.Push(5)
|
||||
s.Pop()
|
||||
|
||||
if s.Len() != 2 {
|
||||
t.Errorf("stack len not correct after pop:\n+++ got: %d\n--- want: %d",
|
||||
s.Len(), 2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestShift(t *testing.T) {
|
||||
t.Run("shift", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
s.Shift()
|
||||
|
||||
if s.Len() != 0 {
|
||||
t.Errorf("stack not empty after shift()")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestClear(t *testing.T) {
|
||||
t.Run("clear", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
s.Push(5)
|
||||
s.Push(5)
|
||||
s.Push(5)
|
||||
s.Clear()
|
||||
|
||||
if s.Len() != 0 {
|
||||
t.Errorf("stack not empty after clear()")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLast(t *testing.T) {
|
||||
t.Run("last", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
s.Push(5)
|
||||
got := s.Last()
|
||||
|
||||
if len(got) != 1 {
|
||||
t.Errorf("last failed:\n+++ got: %d elements\n--- want: %d elements",
|
||||
len(got), 1)
|
||||
}
|
||||
|
||||
if got[0] != 5.0 {
|
||||
t.Errorf("last failed:\n+++ got: %f\n--- want: %f",
|
||||
got, 5.0)
|
||||
}
|
||||
|
||||
if s.Len() != 1 {
|
||||
t.Errorf("stack modified after last()")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
t.Run("all", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
list := []float64{2, 4, 6, 8}
|
||||
|
||||
for _, item := range list {
|
||||
s.Push(item)
|
||||
}
|
||||
|
||||
got := s.All()
|
||||
|
||||
if len(got) != len(list) {
|
||||
t.Errorf("all failed:\n+++ got: %d elements\n--- want: %d elements",
|
||||
len(got), len(list))
|
||||
}
|
||||
|
||||
for i := 1; i < len(list); i++ {
|
||||
if got[i] != list[i] {
|
||||
t.Errorf("all failed (element %d):\n+++ got: %f\n--- want: %f",
|
||||
i, got[i], list[i])
|
||||
}
|
||||
}
|
||||
|
||||
if s.Len() != len(list) {
|
||||
t.Errorf("stack modified after last()")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBackupRestore(t *testing.T) {
|
||||
t.Run("shift", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
s.Push(5)
|
||||
s.Backup()
|
||||
s.Clear()
|
||||
s.Restore()
|
||||
|
||||
if s.Len() != 1 {
|
||||
t.Errorf("stack not correctly restored()")
|
||||
}
|
||||
|
||||
a := s.Pop()
|
||||
if a != 5.0 {
|
||||
t.Errorf("stack not identical to old revision:\n+++ got: %f\n--- want: %f",
|
||||
a, 5.0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestReverse(t *testing.T) {
|
||||
t.Run("reverse", func(t *testing.T) {
|
||||
s := NewStack()
|
||||
list := []float64{2, 4, 6}
|
||||
reverse := []float64{6, 4, 2}
|
||||
|
||||
for _, item := range list {
|
||||
s.Push(item)
|
||||
}
|
||||
|
||||
s.Reverse()
|
||||
|
||||
got := s.All()
|
||||
|
||||
if len(got) != len(list) {
|
||||
t.Errorf("all failed:\n+++ got: %d elements\n--- want: %d elements",
|
||||
len(got), len(list))
|
||||
}
|
||||
|
||||
for i := 1; i < len(reverse); i++ {
|
||||
if got[i] != reverse[i] {
|
||||
t.Errorf("reverse failed (element %d):\n+++ got: %f\n--- want: %f",
|
||||
i, got[i], list[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
32
util_test.go
Normal file
32
util_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright © 2023 Thomas von Dein
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
list := []string{"a", "b", "c"}
|
||||
|
||||
t.Run("contains", func(t *testing.T) {
|
||||
if !contains(list, "a") {
|
||||
t.Errorf("a in [a,b,c] not found")
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user