Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
id-token: write
steps:
- name: Check out the repo
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: stable
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v8
with:
version: v1.60
version: v2.4
args: --timeout=1m
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ["1.23"]
go-version: ["1.24.7", "1.25.1"]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go-version }}
- name: Install dependencies
Expand Down
87 changes: 40 additions & 47 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,53 +1,46 @@
run:
timeout: 30s

output:
sort-results: true
sort-order:
- file
version: "2"

linters:
default: all
disable:
# Annoying linters
- depguard # Needs configuration
- ireturn # Forbids returning interface values
- nlreturn # Requires a newline before return statements
- exhaustruct # Requires all struct fields to be initialized
- nonamedreturns # Forbids named return values
- wsl_v5 # Enforces particular statement grouping using blank lines
- noinlineerr # Forbids inline error handling
- varnamelen # Forbids short variable names
- err113 # Forbids dynamic error declaration without wrapping
- nilnil # Forbids `return nil, nil` as not idiomatic
- paralleltest # Requires parallel tests
- tagliatelle # Enforces a specific struct tag casing
- testpackage # Requires tests to be in a package named `*_test`
- funlen # Enforces function length limits
- godox # Forbids TODO/FIXME comments
- promlinter # Enforces a naming convention
# Others
- wsl # deprecated (since v2.2.0) due to: new major version. Replaced by wsl_v5.
- cyclop # We already use gocyclo
- musttag # Too many false positives
- mnd # None of the numbers should be magic
- lll # Line width is enforced with exceptions
settings:
gocyclo:
min-complexity: 12
nestif:
min-complexity: 6

formatters:
enable:
- bodyclose
- containedctx
- contextcheck
- copyloopvar
- decorder
- dupl
- dupword
- durationcheck
- errchkjson
- errname
- errorlint
- gochecknoglobals
- goconst
- goimports
- gocritic
- gocyclo
- gosec
- makezero
- mirror
- nestif
- nilerr
- perfsprint
- prealloc
- revive
- stylecheck
- unconvert
- unused
- usestdlibvars
- wastedassign
- whitespace

linters-settings:
gocyclo:
min-complexity: 12
nestif:
min-complexity: 6
output:
sort-order:
- file
- linter
show-stats: true

issues:
include:
# don't suppress missing exported method/function parameters
- EXC0012
# don't suppress missing package level comment
- EXC0015
run:
timeout: 5m
11 changes: 7 additions & 4 deletions cmd/convert/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// This package allows to convert datamodels into a CSV format that is supported
// by this simulator.
// nolint:gochecknoglobals
//
//nolint:gochecknoglobals,gosec
package main

import (
Expand Down Expand Up @@ -63,7 +64,11 @@ func save(path string, params []datamodel.Parameter) error {
if err != nil {
return fmt.Errorf("create destination file: %s", path)
}
defer fd.Close()
defer func() {
if err := fd.Close(); err != nil {
log.Error("Failed to close file", log.Cause(err), log.F{"path": path})
}
}()

slices.SortFunc(params, func(a, b datamodel.Parameter) int {
return cmp.Compare(strings.ToLower(a.Path), strings.ToLower(b.Path))
Expand All @@ -88,8 +93,6 @@ func save(path string, params []datamodel.Parameter) error {
return nil
}

// FIXME: cyclo complexity is too high, rewrite it
// nolint:gocyclo,musttag
func convertGetParameterValuesResponse(b []byte) []datamodel.Parameter {
var gpv struct {
XMLName xml.Name `xml:"GetParameterValuesResponse"`
Expand Down
3 changes: 2 additions & 1 deletion datamodel/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ func (dm *DataModel) UDPConnectionRequestAddress() Parameter {
return p
}

// SetConnectionRequestURL sets UDP connection request address to the given value.
// SetUDPConnectionRequestAddress sets UDP connection request address to the
// given value.
func (dm *DataModel) SetUDPConnectionRequestAddress(val string) {
dm.SetValue(pathUDPConnectionRequestAddress, val)
}
Expand Down
3 changes: 2 additions & 1 deletion datamodel/datamodel.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,6 @@ func (dm *DataModel) DeleteObject(name string) {

// ParameterNames returns all subparameters in the given path. If nextLevel is
// set to true the list of parameters goes one level deeper.
// nolint:nestif
func (dm *DataModel) ParameterNames(path string, nextLevel bool) []Parameter {
var reg *regexp.Regexp
if path == "" {
Expand Down Expand Up @@ -381,6 +380,8 @@ func (dm *DataModel) prefixedPath(path string) string {
return path
}
return tr181Prefix + path
case unknownVersion:
fallthrough
default:
return path
}
Expand Down
12 changes: 6 additions & 6 deletions datamodel/datamodel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func TestGetValueFormatGen(t *testing.T) {
}))
param, ok := dm.GetValue(path2)
assert.True(t, ok)
assert.Equal(t, "", param.GetValue())
assert.Empty(t, param.GetValue())
})
}

Expand Down Expand Up @@ -277,22 +277,22 @@ func TestAddObject(t *testing.T) {
dm := New(newState())
dm.SetValue("Device.DeviceInfo.Description", "Residential Gateway")
_, err := dm.AddObject("Device.DeviceInfo.Description")
assert.NotNil(t, err)
require.Error(t, err)
assert.Equal(t, errors.New("parent is not an object"), err)
}

func TestAddObjectNonExistentParent(t *testing.T) {
dm := New(newState())
_, err := dm.AddObject("Device.NonExistent.Parent")
assert.NotNil(t, err)
require.Error(t, err)
assert.Equal(t, errors.New("parent object doesn't exist"), err)
}

func TestAddObjectParentNotObject(t *testing.T) {
dm := New(newState())
dm.SetValue("Device.DeviceInfo", "Some Value")
_, err := dm.AddObject("Device.DeviceInfo")
assert.NotNil(t, err)
require.Error(t, err)
assert.Equal(t, errors.New("parent is not an object"), err)
}

Expand Down Expand Up @@ -320,7 +320,7 @@ func TestParameterNamesEmptyPath(t *testing.T) {
},
}))
params := dm.ParameterNames("", true)
assert.Len(t, params, 0)
assert.Empty(t, params)
}

func TestParameterNamesNoMatch(t *testing.T) {
Expand All @@ -332,7 +332,7 @@ func TestParameterNamesNoMatch(t *testing.T) {
},
}))
params := dm.ParameterNames("Device.Ethernet", true)
assert.Len(t, params, 0)
assert.Empty(t, params)
}

func TestParameterNamesNextLevel(t *testing.T) {
Expand Down
13 changes: 4 additions & 9 deletions datamodel/noise/generator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Package noise provides algorithms to generate sequences of values that
// simulate sensor readings with various patterns, including random walk,
// piecewise linear, sine wave with noise, and Perlin noise.
//
//nolint:gosec
package noise

import (
Expand All @@ -18,8 +20,6 @@ import (
func RandomWalk(startValue, minValue, maxValue, step float64) Generator {
prevValue := startValue
return func() float64 {
// nolint:gosec
// It's okay to use the default random number generator here.
change := (rand.Float64()*2 - 1) * step
newValue := clamp(prevValue+change, minValue, maxValue)
prevValue = newValue
Expand All @@ -39,8 +39,6 @@ func PiecewiseLinear(startValue, minValue, maxValue, step float64) Generator {
if i%20 == 0 {
direction *= -1
}
// nolint:gosec
// It's okay to use the default random number generator here.
change := direction*step + (rand.Float64()*2-1)*(step/2)
newValue := clamp(prevValue+change, minValue, maxValue)
prevValue = newValue
Expand All @@ -67,8 +65,6 @@ func PiecewiseLinear(startValue, minValue, maxValue, step float64) Generator {
func SineWithNoise(offset, amplitude, frequency, phase, noiseScale float64) Generator {
i := 0
return func() float64 {
// nolint:gosec
// It's okay to use the default random number generator here.
value := offset + amplitude*math.Sin(frequency*float64(i)+phase) + rand.Float64()*noiseScale
i++
return value
Expand All @@ -87,10 +83,11 @@ func SineWithNoise(offset, amplitude, frequency, phase, noiseScale float64) Gene
// - scale: A scaling factor to adjust the amplitude of the noise.
// - offset: A constant value to be added to the generated noise values.
func PerlinNoise(offset, alpha, beta float64, scale float64) Generator {
const noiseFactor = 0.1
p := perlin.NewPerlin(alpha, beta, 3, time.Now().UnixNano())
i := 0
return func() float64 {
value := p.Noise1D(float64(i) * 0.1)
value := p.Noise1D(float64(i) * noiseFactor)
i++
return offset + scale*value
}
Expand All @@ -110,8 +107,6 @@ func TrendWithNoise(startValue, step, noiseScale float64) func() float64 {
prevValue := startValue
return func() float64 {
newValue := prevValue + step
// nolint:gosec
// It's okay to use the default random number generator here.
noise := (rand.Float64()*2 - 1) * noiseScale
if step < 0 {
newValue = min(newValue, newValue+noise)
Expand Down
20 changes: 10 additions & 10 deletions datamodel/noise/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const (

func TestRandomWalkBounds(t *testing.T) {
gen := RandomWalk(startValue, minValue, maxValue, step)
for i := 0; i < 100; i++ {
for range 100 {
value := gen()
if value < minValue || value > maxValue {
t.Errorf("Value out of bounds: got %v, want between %v and %v", value, minValue, maxValue)
Expand All @@ -31,7 +31,7 @@ func TestRandomWalkBounds(t *testing.T) {

func TestPiecewiseLinearBounds(t *testing.T) {
gen := PiecewiseLinear(startValue, minValue, maxValue, step)
for i := 0; i < 100; i++ {
for range 100 {
value := gen()
if value < minValue || value > maxValue {
t.Errorf("Value out of bounds: got %v, want between %v and %v", value, minValue, maxValue)
Expand All @@ -41,7 +41,7 @@ func TestPiecewiseLinearBounds(t *testing.T) {

func TestSineWithNoiseBounds(t *testing.T) {
gen := SineWithNoise(offset, amplitude, frequency, phase, noiseScale)
for i := 0; i < 100; i++ {
for range 100 {
value := gen()
// Since sine wave values range between -amplitude and +amplitude, we add noiseScale to the bounds
if value < -amplitude-noiseScale || value > amplitude+noiseScale {
Expand All @@ -52,7 +52,7 @@ func TestSineWithNoiseBounds(t *testing.T) {

func TestPerlinNoiseBounds(t *testing.T) {
gen := PerlinNoise(offset, alpha, beta, scale)
for i := 0; i < 100; i++ {
for range 100 {
value := gen()
// Perlin noise values are typically between -1 and 1, scaled and offset
if value < -scale+offset || value > scale+offset {
Expand All @@ -63,7 +63,7 @@ func TestPerlinNoiseBounds(t *testing.T) {

func TestTrendWithNoiseBounds(t *testing.T) {
gen := TrendWithNoise(startValue, step, noiseScale)
for i := 0; i < 100; i++ {
for range 100 {
value := gen()
// Since the trend can go indefinitely, we only check that the noise does not exceed the noiseScale
if step >= 0 && value < startValue || step < 0 && value > startValue {
Expand All @@ -74,36 +74,36 @@ func TestTrendWithNoiseBounds(t *testing.T) {

func BenchmarkRandomWalk(b *testing.B) {
gen := RandomWalk(startValue, minValue, maxValue, step)
for i := 0; i < b.N; i++ {
for b.Loop() {
gen()
}
}

func BenchmarkPiecewiseLinear(b *testing.B) {
gen := PiecewiseLinear(startValue, minValue, maxValue, step)
for i := 0; i < b.N; i++ {
for b.Loop() {
gen()
}
}

func BenchmarkSineWithNoise(b *testing.B) {
gen := SineWithNoise(offset, amplitude, frequency, phase, noiseScale)
for i := 0; i < b.N; i++ {
for b.Loop() {
gen()
}
}

func BenchmarkPerlinNoise(b *testing.B) {
gen := PerlinNoise(offset, alpha, beta, scale)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
gen()
}
}

func BenchmarkTrendWithNoise(b *testing.B) {
gen := TrendWithNoise(startValue, step, noiseScale)
for i := 0; i < b.N; i++ {
for b.Loop() {
gen()
}
}
Loading