mirror of
https://git.numenor-labs.us/dsfx.git
synced 2025-04-29 00:00:35 +00:00
refactor(project): rethink directory structure
This commit is contained in:
parent
1876033e13
commit
330fa5dd37
1
CHANGELOG.md
Normal file
1
CHANGELOG.md
Normal file
@ -0,0 +1 @@
|
||||
# Changelog
|
@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"context"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/cmd/dsfxctl/client"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/system"
|
||||
"koti.casa/numenor-labs/dsfx/internal/client/client"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/system"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -4,10 +4,11 @@ import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/cmd/dsfxnode/node"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/storage/scoped"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/system"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/system"
|
||||
"koti.casa/numenor-labs/dsfx/internal/peer/node"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/storage/scoped"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -8,13 +8,14 @@ import (
|
||||
"log/slog"
|
||||
"net"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/cmd/dsfxctl/conf"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/logging"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/network"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/storage/scoped"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/system"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/logging"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/network"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/storage/scoped"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/system"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/internal/client/conf"
|
||||
)
|
||||
|
||||
// Client represents the client application for dsfxctl.
|
@ -1,6 +1,6 @@
|
||||
package conf
|
||||
|
||||
import "koti.casa/numenor-labs/dsfx/pkg/system"
|
||||
import "koti.casa/numenor-labs/dsfx/internal/lib/system"
|
||||
|
||||
const (
|
||||
// DefaultConfigDir is the default directory for the dsfxctl configuration.
|
@ -4,7 +4,7 @@ import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/encryption"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/encryption"
|
||||
)
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
@ -3,7 +3,7 @@ package disk_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
)
|
||||
|
||||
func TestDefaultDisk(t *testing.T) {
|
@ -5,7 +5,7 @@ import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/frame"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/frame"
|
||||
)
|
||||
|
||||
func TestLenPrefixedWriteTo(t *testing.T) {
|
7
internal/lib/handshake/bench.txt
Normal file
7
internal/lib/handshake/bench.txt
Normal file
@ -0,0 +1,7 @@
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: koti.casa/numenor-labs/dsfx/internal/lib/handshake
|
||||
cpu: Intel(R) Core(TM) Ultra 9 185H
|
||||
BenchmarkHandshake 4508 285270 ns/op 12976 B/op 131 allocs/op
|
||||
PASS
|
||||
ok koti.casa/numenor-labs/dsfx/internal/lib/handshake 1.291s
|
@ -10,12 +10,12 @@ import (
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/assert"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/buffer"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/encryption"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/keyexchange"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/logging"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/assert"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/buffer"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/encryption"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/keyexchange"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/logging"
|
||||
)
|
||||
|
||||
const (
|
@ -10,8 +10,8 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/handshake"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/handshake"
|
||||
)
|
||||
|
||||
func TestHandshake(t *testing.T) {
|
@ -8,7 +8,7 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/identity"
|
||||
)
|
||||
|
||||
var (
|
@ -5,8 +5,8 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/encryption"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/frame"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/encryption"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/frame"
|
||||
)
|
||||
|
||||
// Conn is a wrapper around net.TCPConn that encrypts and decrypts data as it is
|
@ -6,8 +6,8 @@ import (
|
||||
"log/slog"
|
||||
"net"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/handshake"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/handshake"
|
||||
)
|
||||
|
||||
// Listener ...
|
@ -5,8 +5,8 @@ import (
|
||||
"crypto/ed25519"
|
||||
"net"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/handshake"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/logging"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/handshake"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/logging"
|
||||
)
|
||||
|
||||
// Dial ...
|
@ -4,7 +4,7 @@ import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
)
|
||||
|
||||
// StorageScope is an interface that extends the disk.Disk interface by ensuring
|
@ -4,8 +4,8 @@ import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/storage/scoped"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/storage/scoped"
|
||||
)
|
||||
|
||||
func TestScopedStorage_Scope(t *testing.T) {
|
@ -3,7 +3,7 @@ package system
|
||||
import (
|
||||
"os"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
)
|
||||
|
||||
// Default returns a default implementation of the System interface.
|
@ -1,7 +1,7 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
)
|
||||
|
||||
type System interface {
|
@ -1,6 +1,6 @@
|
||||
package conf
|
||||
|
||||
import "koti.casa/numenor-labs/dsfx/pkg/system"
|
||||
import "koti.casa/numenor-labs/dsfx/internal/lib/system"
|
||||
|
||||
const (
|
||||
// DefaultConfigDir is the default directory for the dsfxctl configuration.
|
@ -10,12 +10,13 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/cmd/dsfxnode/conf"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/disk"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/logging"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/network"
|
||||
"koti.casa/numenor-labs/dsfx/pkg/system"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/logging"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/network"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/system"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/internal/peer/conf"
|
||||
)
|
||||
|
||||
type Node struct {
|
287
internal/sim/disk.go
Normal file
287
internal/sim/disk.go
Normal file
@ -0,0 +1,287 @@
|
||||
package sim
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
)
|
||||
|
||||
// Tolerance defines simulation tolerance parameters.
|
||||
type Tolerance struct {
|
||||
// Latency to wait before executing an operation.
|
||||
Latency time.Duration
|
||||
// FailureChance is the probability (0.0–1.0) that an operation fails.
|
||||
FailureChance float64
|
||||
// CorruptionChance is the probability (0.0–1.0) that a write is corrupted.
|
||||
CorruptionChance float64
|
||||
}
|
||||
|
||||
// simEntry represents an entry in our in–memory file system.
|
||||
type simEntry struct {
|
||||
name string
|
||||
isDir bool
|
||||
perm fs.FileMode
|
||||
modTime time.Time
|
||||
|
||||
// For files, data holds the file contents.
|
||||
data []byte
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// simFile is a simulated file object. It implements fs.File and io.Writer.
|
||||
// The same underlying simEntry is shared among all handles to a given file.
|
||||
type simFile struct {
|
||||
entry *simEntry
|
||||
// offset to simulate reading sequentially.
|
||||
offset int
|
||||
// readOnly indicates whether writes are allowed.
|
||||
readOnly bool
|
||||
// closed is set once Close() has been called.
|
||||
closed bool
|
||||
// You could add a mutex here if you want to protect concurrent access per handle.
|
||||
mu sync.Mutex
|
||||
|
||||
// tol copied from the SimDisk for per–file simulation.
|
||||
tol Tolerance
|
||||
}
|
||||
|
||||
// Ensure simFile implements the interfaces.
|
||||
var _ disk.File = (*simFile)(nil)
|
||||
|
||||
// Read reads from the simulated file starting at the current offset.
|
||||
func (sf *simFile) Read(p []byte) (int, error) {
|
||||
sf.mu.Lock()
|
||||
defer sf.mu.Unlock()
|
||||
|
||||
if sf.closed {
|
||||
return 0, errors.New("read from closed file")
|
||||
}
|
||||
|
||||
// Lock the underlying data.
|
||||
sf.entry.mu.Lock()
|
||||
defer sf.entry.mu.Unlock()
|
||||
|
||||
if sf.offset >= len(sf.entry.data) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n := copy(p, sf.entry.data[sf.offset:])
|
||||
sf.offset += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Write writes the given bytes at the end of the file (for a writable file).
|
||||
// It may simulate corruption.
|
||||
func (sf *simFile) Write(p []byte) (int, error) {
|
||||
if sf.readOnly {
|
||||
return 0, errors.New("cannot write to read-only file")
|
||||
}
|
||||
sf.mu.Lock()
|
||||
defer sf.mu.Unlock()
|
||||
|
||||
if sf.closed {
|
||||
return 0, errors.New("write to closed file")
|
||||
}
|
||||
|
||||
// simulate latency
|
||||
time.Sleep(sf.tol.Latency)
|
||||
// simulate failure
|
||||
if rand.Float64() < sf.tol.FailureChance {
|
||||
return 0, errors.New("simulated write failure")
|
||||
}
|
||||
|
||||
// prepare data to write
|
||||
dataToWrite := make([]byte, len(p))
|
||||
copy(dataToWrite, p)
|
||||
|
||||
// simulate corruption by flipping bits in the data (if triggered)
|
||||
if rand.Float64() < sf.tol.CorruptionChance {
|
||||
for i := range dataToWrite {
|
||||
dataToWrite[i] = ^dataToWrite[i] // simple corruption: bitwise complement
|
||||
}
|
||||
}
|
||||
|
||||
// lock the underlying entry and append data
|
||||
sf.entry.mu.Lock()
|
||||
defer sf.entry.mu.Unlock()
|
||||
sf.entry.data = append(sf.entry.data, dataToWrite...)
|
||||
// update modification time
|
||||
sf.entry.modTime = time.Now()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Close marks the file as closed.
|
||||
func (sf *simFile) Close() error {
|
||||
sf.mu.Lock()
|
||||
defer sf.mu.Unlock()
|
||||
if sf.closed {
|
||||
return errors.New("file already closed")
|
||||
}
|
||||
sf.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stat returns file information for the simulated file.
|
||||
func (sf *simFile) Stat() (fs.FileInfo, error) {
|
||||
sf.mu.Lock()
|
||||
defer sf.mu.Unlock()
|
||||
if sf.closed {
|
||||
return nil, errors.New("stat on closed file")
|
||||
}
|
||||
return &simFileInfo{entry: sf.entry}, nil
|
||||
}
|
||||
|
||||
// simFileInfo implements fs.FileInfo for a simEntry.
|
||||
type simFileInfo struct {
|
||||
entry *simEntry
|
||||
}
|
||||
|
||||
var _ fs.FileInfo = (*simFileInfo)(nil)
|
||||
|
||||
func (fi *simFileInfo) Name() string { return fi.entry.name }
|
||||
func (fi *simFileInfo) Size() int64 {
|
||||
fi.entry.mu.Lock()
|
||||
defer fi.entry.mu.Unlock()
|
||||
return int64(len(fi.entry.data))
|
||||
}
|
||||
func (fi *simFileInfo) Mode() fs.FileMode { return fi.entry.perm }
|
||||
func (fi *simFileInfo) ModTime() time.Time { return fi.entry.modTime }
|
||||
func (fi *simFileInfo) IsDir() bool { return fi.entry.isDir }
|
||||
func (fi *simFileInfo) Sys() interface{} { return nil }
|
||||
|
||||
// SimDisk is our in–memory simulation that implements the disk.Disk interface.
|
||||
type SimDisk struct {
|
||||
// tol holds the tolerance parameters to simulate latency, failures, corruption.
|
||||
tol Tolerance
|
||||
|
||||
// entries is a map from file or directory name to its corresponding simEntry.
|
||||
entries map[string]*simEntry
|
||||
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewSimDisk returns a new simulated disk with the given tolerance parameters.
|
||||
func NewSimDisk(tol Tolerance) disk.Disk {
|
||||
return &SimDisk{
|
||||
tol: tol,
|
||||
entries: make(map[string]*simEntry),
|
||||
}
|
||||
}
|
||||
|
||||
// simulateOp sleeps for the configured latency and then returns an error if a failure is simulated.
|
||||
func (sd *SimDisk) simulateOp() error {
|
||||
time.Sleep(sd.tol.Latency)
|
||||
if rand.Float64() < sd.tol.FailureChance {
|
||||
return errors.New("simulated disk operation failure")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mkdir creates a directory in the simulated file system.
|
||||
func (sd *SimDisk) Mkdir(name string, perm fs.FileMode) error {
|
||||
if err := sd.simulateOp(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sd.mu.Lock()
|
||||
defer sd.mu.Unlock()
|
||||
|
||||
if _, exists := sd.entries[name]; exists {
|
||||
return errors.New("directory already exists")
|
||||
}
|
||||
sd.entries[name] = &simEntry{
|
||||
name: name,
|
||||
isDir: true,
|
||||
perm: perm,
|
||||
modTime: time.Now(),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create creates (or truncates) a file in the simulated file system and returns a writable file.
|
||||
func (sd *SimDisk) Create(name string) (disk.File, error) {
|
||||
if err := sd.simulateOp(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sd.mu.Lock()
|
||||
// For simplicity, Create always truncates (or creates new)
|
||||
se := &simEntry{
|
||||
name: name,
|
||||
isDir: false,
|
||||
perm: 0644,
|
||||
modTime: time.Now(),
|
||||
data: []byte{},
|
||||
}
|
||||
sd.entries[name] = se
|
||||
sd.mu.Unlock()
|
||||
|
||||
// Return a writable file handle
|
||||
return &simFile{
|
||||
entry: se,
|
||||
offset: 0,
|
||||
readOnly: false,
|
||||
tol: sd.tol,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Open opens an existing file in the simulated file system.
|
||||
// The returned file is "read-only" (write attempts will error),
|
||||
func (sd *SimDisk) Open(name string) (disk.File, error) {
|
||||
if err := sd.simulateOp(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sd.mu.Lock()
|
||||
se, exists := sd.entries[name]
|
||||
sd.mu.Unlock()
|
||||
if !exists {
|
||||
return nil, errors.New("file does not exist")
|
||||
}
|
||||
if se.isDir {
|
||||
return nil, errors.New("cannot open directory")
|
||||
}
|
||||
|
||||
// Return a new file handle starting at offset 0; marked as readOnly.
|
||||
return &simFile{
|
||||
entry: se,
|
||||
offset: 0,
|
||||
readOnly: true,
|
||||
tol: sd.tol,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Stat returns information about the file or directory.
|
||||
func (sd *SimDisk) Stat(name string) (fs.FileInfo, error) {
|
||||
if err := sd.simulateOp(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sd.mu.Lock()
|
||||
se, exists := sd.entries[name]
|
||||
sd.mu.Unlock()
|
||||
if !exists {
|
||||
return nil, errors.New("no such file or directory")
|
||||
}
|
||||
|
||||
return &simFileInfo{entry: se}, nil
|
||||
}
|
||||
|
||||
// Remove deletes a file or directory from the simulated file system.
|
||||
func (sd *SimDisk) Remove(name string) error {
|
||||
if err := sd.simulateOp(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sd.mu.Lock()
|
||||
defer sd.mu.Unlock()
|
||||
if _, exists := sd.entries[name]; !exists {
|
||||
return errors.New("file or directory does not exist")
|
||||
}
|
||||
delete(sd.entries, name)
|
||||
return nil
|
||||
}
|
219
internal/sim/system.go
Normal file
219
internal/sim/system.go
Normal file
@ -0,0 +1,219 @@
|
||||
package sim
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/disk"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/system"
|
||||
)
|
||||
|
||||
// SimSystem is a simulated implementation of system.System.
|
||||
// It allows the caller to set parameters for latency and failure chance (from tol)
|
||||
// and provides in–memory values for command–line arguments, environment variables,
|
||||
// and standard output/error.
|
||||
type SimSystem struct {
|
||||
// Simulation tolerance parameters (latency, failure chance, corruption chance).
|
||||
tol Tolerance
|
||||
|
||||
// Simulated command–line arguments.
|
||||
args []string
|
||||
|
||||
// In–memory environment variables. Protected by mu.
|
||||
mu sync.Mutex
|
||||
env map[string]string
|
||||
|
||||
// Simulated directory paths.
|
||||
homeDir string
|
||||
configDir string
|
||||
cacheDir string
|
||||
tempDir string
|
||||
|
||||
// Simulated standard output and error.
|
||||
stdout disk.File
|
||||
stderr disk.File
|
||||
}
|
||||
|
||||
// NewSimSystem returns a new simulated system that implements system.System.
|
||||
// The caller provides a tolerance value and a slice of command–line arguments.
|
||||
// Other fields (directories, environment) are pre–populated for simulation purposes.
|
||||
func NewSimSystem(tol Tolerance, args []string) system.System {
|
||||
s := &SimSystem{
|
||||
tol: tol,
|
||||
args: args,
|
||||
env: make(map[string]string),
|
||||
homeDir: "/home/simuser",
|
||||
configDir: "/home/simuser/.config",
|
||||
cacheDir: "/home/simuser/.cache",
|
||||
tempDir: "/tmp",
|
||||
}
|
||||
// Create simulated stdout and stderr.
|
||||
s.stdout = newSimOutput("stdout", tol)
|
||||
s.stderr = newSimOutput("stderr", tol)
|
||||
return s
|
||||
}
|
||||
|
||||
// simulateOp applies the configured latency and possibly simulates an operation failure.
|
||||
func (s *SimSystem) simulateOp() error {
|
||||
time.Sleep(s.tol.Latency)
|
||||
if rand.Float64() < s.tol.FailureChance {
|
||||
return errors.New("simulated system operation failure")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Args returns the simulated command–line arguments (skipping the program name).
|
||||
func (s *SimSystem) Args() []string {
|
||||
// Simulate latency even for argument access.
|
||||
_ = s.simulateOp()
|
||||
// Return a copy so that callers cannot modify the underlying slice.
|
||||
cpy := make([]string, len(s.args))
|
||||
copy(cpy, s.args)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// Arg returns the simulated command–line argument at index i.
|
||||
// If the index is out–of–range, it returns an empty string.
|
||||
func (s *SimSystem) Arg(i int) string {
|
||||
_ = s.simulateOp()
|
||||
if i < 0 || i >= len(s.args) {
|
||||
return ""
|
||||
}
|
||||
return s.args[i]
|
||||
}
|
||||
|
||||
// UserHomeDir returns the simulated home directory.
|
||||
func (s *SimSystem) UserHomeDir() (string, error) {
|
||||
if err := s.simulateOp(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.homeDir, nil
|
||||
}
|
||||
|
||||
// UserConfigDir returns the simulated configuration directory.
|
||||
func (s *SimSystem) UserConfigDir() (string, error) {
|
||||
if err := s.simulateOp(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.configDir, nil
|
||||
}
|
||||
|
||||
// UserCacheDir returns the simulated cache directory.
|
||||
func (s *SimSystem) UserCacheDir() (string, error) {
|
||||
if err := s.simulateOp(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.cacheDir, nil
|
||||
}
|
||||
|
||||
// TempDir returns the simulated temporary directory.
|
||||
func (s *SimSystem) TempDir() string {
|
||||
// We simulate latency even though TempDir cannot fail.
|
||||
_ = s.simulateOp()
|
||||
return s.tempDir
|
||||
}
|
||||
|
||||
// Stdout returns a simulated disk.File representing standard output.
|
||||
func (s *SimSystem) Stdout() disk.File {
|
||||
// In a real simulation you might add latency/failure to writes on stdout.
|
||||
_ = s.simulateOp()
|
||||
return s.stdout
|
||||
}
|
||||
|
||||
// Stderr returns a simulated disk.File representing standard error.
|
||||
func (s *SimSystem) Stderr() disk.File {
|
||||
_ = s.simulateOp()
|
||||
return s.stderr
|
||||
}
|
||||
|
||||
// Exit simulates terminating the program with the given exit code.
|
||||
// As with many tests, we simulate exit by panicing with a special error.
|
||||
// This allows tests to catch the panic and inspect the exit code.
|
||||
func (s *SimSystem) Exit(code int) {
|
||||
_ = s.simulateOp()
|
||||
panic(&SimExitError{Code: code})
|
||||
}
|
||||
|
||||
// GetEnv retrieves the simulated value for the environment variable named by key.
|
||||
func (s *SimSystem) GetEnv(key string) string {
|
||||
_ = s.simulateOp()
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.env[key]
|
||||
}
|
||||
|
||||
// SetEnv sets the simulated environment variable named by key to value.
|
||||
func (s *SimSystem) SetEnv(key, value string) error {
|
||||
_ = s.simulateOp()
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.env[key] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// SimExitError is the error value used to simulate a program exit.
|
||||
type SimExitError struct {
|
||||
Code int
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *SimExitError) Error() string {
|
||||
return "simulated program exit"
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Helper functions and types for simulated standard output/error
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// newSimOutput creates a new simulated disk.File that acts as an output.
|
||||
// Internally it creates a simulated file with an in–memory entry.
|
||||
func newSimOutput(name string, tol Tolerance) disk.File {
|
||||
entry := &simEntry{
|
||||
name: name,
|
||||
isDir: false,
|
||||
perm: 0644,
|
||||
modTime: time.Now(),
|
||||
data: []byte{},
|
||||
}
|
||||
// Return a simFile which implements disk.File.
|
||||
// (Note: simFile and simEntry are the types defined in dsfx/sim/disk.go.)
|
||||
return &simFile{
|
||||
entry: entry,
|
||||
offset: 0,
|
||||
readOnly: false,
|
||||
tol: tol,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Usage example:
|
||||
|
||||
// Define a tolerance for simulation.
|
||||
tol := sim.Tolerance{
|
||||
Latency: 10 * time.Millisecond,
|
||||
FailureChance: 0.01,
|
||||
CorruptionChance: 0.005,
|
||||
}
|
||||
|
||||
// Simulate a system with custom command–line arguments.
|
||||
sys := sim.NewSimSystem(tol, []string{"--verbose", "--config=sim.conf"})
|
||||
|
||||
// Calling system methods:
|
||||
args := sys.Args()
|
||||
home, err := sys.UserHomeDir()
|
||||
// ... etc.
|
||||
|
||||
// To simulate an exit:
|
||||
func run() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if exitErr, ok := r.(*sim.SimExitError); ok {
|
||||
fmt.Printf("simulated exit(%d)\n", exitErr.Code)
|
||||
}
|
||||
}
|
||||
}()
|
||||
sys.Exit(3)
|
||||
}
|
||||
*/
|
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := exec.Command("go", "test", "-bench=Handshake", "-cpu=1", "-benchmem", "./pkg/handshake/...")
|
||||
cmd := exec.Command("go", "test", "-bench=Handshake", "-cpu=1", "-benchmem", "./internal/lib/handshake/...")
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"koti.casa/numenor-labs/dsfx/pkg/crypto/identity"
|
||||
"koti.casa/numenor-labs/dsfx/internal/lib/crypto/identity"
|
||||
)
|
||||
|
||||
func main() {
|
@ -1,7 +0,0 @@
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: koti.casa/numenor-labs/dsfx/pkg/handshake
|
||||
cpu: Intel(R) Core(TM) Ultra 9 185H
|
||||
BenchmarkHandshake 4214 274337 ns/op 12976 B/op 131 allocs/op
|
||||
PASS
|
||||
ok koti.casa/numenor-labs/dsfx/pkg/handshake 1.163s
|
41
revive.toml
41
revive.toml
@ -1,41 +0,0 @@
|
||||
ignoreGeneratedHeader = false
|
||||
severity = "warning"
|
||||
confidence = 0.8
|
||||
errorCode = 1
|
||||
warningCode = 1
|
||||
|
||||
[rule.bare-return]
|
||||
[rule.blank-imports]
|
||||
[rule.context-as-argument]
|
||||
[rule.context-keys-type]
|
||||
[rule.dot-imports]
|
||||
[rule.empty-block]
|
||||
[rule.empty-lines]
|
||||
[rule.enforce-map-style]
|
||||
[rule.enforce-slice-style]
|
||||
[rule.error-naming]
|
||||
[rule.error-return]
|
||||
[rule.error-strings]
|
||||
[rule.errorf]
|
||||
[rule.exported]
|
||||
[rule.filename-format]
|
||||
# Override the default pattern to forbid .go files with uppercase letters and dashes.
|
||||
arguments=["^[_a-z][_a-z0-9]*\\.go$"]
|
||||
[rule.increment-decrement]
|
||||
[rule.indent-error-flow]
|
||||
[rule.line-length-limit]
|
||||
arguments = [200]
|
||||
# [rule.package-comments]
|
||||
[rule.range]
|
||||
[rule.receiver-naming]
|
||||
[rule.redefines-builtin-id]
|
||||
[rule.superfluous-else]
|
||||
[rule.time-naming]
|
||||
[rule.unexported-naming]
|
||||
[rule.unexported-return]
|
||||
[rule.unreachable-code]
|
||||
[rule.unused-parameter]
|
||||
[rule.useless-break]
|
||||
[rule.use-any]
|
||||
[rule.var-declaration]
|
||||
[rule.var-naming]
|
Loading…
x
Reference in New Issue
Block a user