Преглед на файлове

SVI Доработка типов; 100.0%

SVI преди 9 месеца
родител
ревизия
018d619bda
променени са 7 файла, в които са добавени 220 реда и са изтрити 10 реда
  1. 48 0
      v2/kc/safe_int/safe_int.go
  2. 101 0
      v2/kc/safe_int/safe_int_test.go
  3. 3 3
      v2/kern_test.go
  4. 15 1
      v2/krn/kmodule/kmodule_test.go
  5. 4 0
      v2/krn/ktypes/isafe_int.go
  6. 26 6
      v2/krn/ktypes/kresult.go
  7. 23 0
      v2/krn/ktypes/kresult_test.go

+ 48 - 0
v2/kc/safe_int/safe_int.go

@@ -2,6 +2,9 @@
 package safe_int
 
 import (
+	"fmt"
+	"os"
+	"strconv"
 	"sync"
 
 	. "gitp78su.ipnodns.ru/svi/kern/v2/krn/ktypes"
@@ -19,6 +22,28 @@ func NewSafeInt() ISafeInt {
 	return sf
 }
 
+// NewSafeIntFromStr -- возвращает новое потокобезоппсное целое из строки
+func NewSafeIntFromStr(strVal string) Result[ISafeInt] {
+	sf := NewSafeInt()
+	res := sf.FromStr(strVal)
+	if res.IsErr() {
+		err := fmt.Errorf("NewSafeIntFromStr(): in parse str(%v), err=\n\t%w", strVal, res.Err())
+		return NewErr[ISafeInt](err)
+	}
+	return NewOk(sf)
+}
+
+// NewSafeIntGetenv -- возвращает новое потокобезоппсное целое из окружения
+func NewSafeIntGetenv(env string) Result[ISafeInt] {
+	sf := NewSafeInt()
+	res := sf.Getenv(env)
+	if res.IsErr() {
+		err := fmt.Errorf("NewSafeIntGetenv(): in parse env(%v), err=\n\t%w", env, res.Err())
+		return NewErr[ISafeInt](err)
+	}
+	return NewOk(sf)
+}
+
 // Get -- возвращает хранимое целое
 func (sf *safeInt) Get() int {
 	sf.RLock()
@@ -39,3 +64,26 @@ func (sf *safeInt) Reset() {
 	defer sf.Unlock()
 	sf.val = 0
 }
+
+// FromStr -- получает число из строки
+func (sf *safeInt) FromStr(strVal string) Result[int] {
+	iVal, err := strconv.Atoi(strVal)
+	if err != nil {
+		return NewErr[int](fmt.Errorf("safeInt.FromStr(): vak(%v) bad, err=\n\t%w", strVal, err))
+	}
+	sf.Lock()
+	defer sf.Unlock()
+	sf.val = iVal
+	return NewOk(sf.val)
+}
+
+// Getenv -- получает значение из окружения
+func (sf *safeInt) Getenv(env string) Result[int] {
+	strVal := os.Getenv(env)
+	res := sf.FromStr(strVal)
+	if res.IsErr() {
+		err := fmt.Errorf("safeInt.Getenv(): from env %v, err=\n\t%w", env, res.Err())
+		return NewErr[int](err)
+	}
+	return res
+}

+ 101 - 0
v2/kc/safe_int/safe_int_test.go

@@ -1,6 +1,7 @@
 package safe_int
 
 import (
+	"os"
 	"testing"
 
 	. "gitp78su.ipnodns.ru/svi/kern/v2/krn/ktypes"
@@ -16,8 +17,108 @@ func TestSafeInt(t *testing.T) {
 		t: t,
 	}
 	sf.new()
+	sf.newGetenv()
+	sf.newFromStr()
 	sf.set()
 	sf.reset()
+	sf.fromStr()
+	sf.getEnv()
+}
+
+// Получает целое из строки
+func (sf *tester) newFromStr() {
+	sf.t.Log("newFromStr")
+	res := NewSafeIntFromStr("-a52")
+	if res.IsOk() {
+		sf.t.Fatalf("newFromStr(): err==nil")
+	}
+	res = NewSafeIntFromStr("-60")
+	if res.IsErr() {
+		sf.t.Fatalf("newFromStr(): err=%v", res.Err())
+	}
+	if val := res.Val(); val.Get() != -60 {
+		sf.t.Fatalf("newFromStr(): val(%v)!=-60", val.Get())
+	}
+}
+
+// Получает целое из окружения
+func (sf *tester) newGetenv() {
+	sf.t.Log("newGetenv")
+	res := NewSafeIntGetenv("TEST_ENV2")
+	if res.IsOk() {
+		sf.t.Fatalf("newGetenv(): err==nil")
+	}
+	os.Unsetenv("TEST_ENV2")
+	os.Setenv("TEST_ENV2", "-52")
+	res = NewSafeIntGetenv("TEST_ENV2")
+	if res.IsErr() {
+		sf.t.Fatalf("newGetenv(): err=%v", res.Err())
+	}
+	if val := res.Val(); val.Get() != -52 {
+		sf.t.Fatalf("newGetenv(): val(%v)!=-52", val.Get())
+	}
+}
+
+// Получает значение из окружения
+func (sf *tester) getEnv() {
+	sf.t.Log("getEnv")
+	sf.getEnvBad1()
+	sf.getEnvGood1()
+}
+
+func (sf *tester) getEnvGood1() {
+	sf.t.Log("getEnvGood1")
+	os.Unsetenv("TEST_VAL")
+	os.Setenv("TEST_VAL", "45")
+	val := NewSafeInt()
+	res := val.Getenv("TEST_VAL")
+	if res.IsErr() {
+		sf.t.Fatalf("getEnvGood1(): err=%v", res.Err())
+	}
+	if val := res.Val(); val != 45 {
+		sf.t.Fatalf("getEnvGood1(): val(%v)!=45", val)
+	}
+}
+
+// Окружение не число
+func (sf *tester) getEnvBad1() {
+	sf.t.Log("getEnvBad1")
+	os.Unsetenv("TEST_VAL")
+	os.Setenv("TEST_VAL", "")
+	val := NewSafeInt()
+	res := val.Getenv("TEST_VAL")
+	if res.IsOk() {
+		sf.t.Fatalf("getEnvBad1(): res==ok")
+	}
+}
+
+// Получает значение из строки
+func (sf *tester) fromStr() {
+	sf.t.Log("fromStr")
+	sf.fromStrBad1()
+	sf.fromStrGood1()
+}
+
+func (sf *tester) fromStrGood1() {
+	sf.t.Log("fromStrGood1")
+	val := NewSafeInt()
+	res := val.FromStr("45")
+	if res.IsErr() {
+		sf.t.Fatalf("fromStrGood1(): err=%v", res.Err())
+	}
+	if val := res.Val(); val != 45 {
+		sf.t.Fatalf("fromStrGood1(): val(%v)!=45", val)
+	}
+}
+
+// Строка не число
+func (sf *tester) fromStrBad1() {
+	sf.t.Log("fromStrBad1")
+	val := NewSafeInt()
+	res := val.FromStr(" ")
+	if res.IsOk() {
+		sf.t.Fatalf("fromStrBad1(): res==ok")
+	}
 }
 
 // Сбрасывает хранимое значение

+ 3 - 3
v2/kern_test.go

@@ -19,15 +19,15 @@ func TestBuilders(t *testing.T) {
 		me: mock_env.MakeEnv(),
 	}
 	_ = os.Unsetenv("LOCAL_STORE_PATH")
-	_ = os.Setenv("LOCAL_STORE_PATH", "/store/store_builder")
+	_ = os.Setenv("LOCAL_STORE_PATH", "/store/store_kern")
 	_ = os.Unsetenv("LOCAL_HTTP_URL")
 	os.Setenv("LOCAL_HTTP_URL", "http://localhost:18311/")
 	fnClear := func() {
-		pwd := sf.me.Pwd() + "/store/store_builder"
+		pwd := sf.me.Pwd() + "/store/store_kern"
 		_ = os.RemoveAll(pwd)
 	}
 	fnClear()
-	fnClear()
+	defer fnClear()
 	sf.new()
 	sf.newModBad()
 }

+ 15 - 1
v2/krn/kmodule/kmodule_test.go

@@ -1,22 +1,36 @@
 package kmodule
 
 import (
+	"os"
 	"testing"
 	"time"
 
 	"gitp78su.ipnodns.ru/svi/kern/v2/krn/kctx"
 	. "gitp78su.ipnodns.ru/svi/kern/v2/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/v2/mock/mock_env"
 )
 
 type tester struct {
 	t   *testing.T
 	mod IKernelModule
+	me  *mock_env.MockEnv
 }
 
 func TestKernelModule(t *testing.T) {
 	sf := &tester{
-		t: t,
+		t:  t,
+		me: mock_env.MakeEnv(),
 	}
+	_ = os.Unsetenv("LOCAL_STORE_PATH")
+	_ = os.Setenv("LOCAL_STORE_PATH", "/store/store_module")
+	_ = os.Unsetenv("LOCAL_HTTP_URL")
+	os.Setenv("LOCAL_HTTP_URL", "http://localhost:18331/")
+	fnClear := func() {
+		pwd := sf.me.Pwd() + "/store/store_module"
+		_ = os.RemoveAll(pwd)
+	}
+	fnClear()
+	defer fnClear()
 	sf.new()
 	sf.run()
 	sf.isWork()

+ 4 - 0
v2/krn/ktypes/isafe_int.go

@@ -8,4 +8,8 @@ type ISafeInt interface {
 	Set(int)
 	// Reset -- сбрасывает хранимое потокобезопасное значение
 	Reset()
+	// FromStr -- устанавливет значение из строки
+	FromStr(string) Result[int]
+	// Getenv -- устанавливает зачение из окружения
+	Getenv(string) Result[int]
 }

+ 26 - 6
v2/krn/ktypes/kresult.go

@@ -3,6 +3,7 @@ package ktypes
 import (
 	"fmt"
 	"reflect"
+	"runtime"
 
 	. "gitp78su.ipnodns.ru/svi/kern/v2/kc/helpers"
 )
@@ -11,9 +12,10 @@ import (
 //
 // Может быть либо только полезное значение, либо только ошибка
 type Result[T any] struct {
-	val   T // Полезное значение
-	isErr bool
-	err   error // Ошибка
+	val     T     // Полезное значение
+	isErr   bool  // Если содержит ошибку
+	isCheck bool  // Если проверено
+	err     error // Ошибка
 }
 
 // NewOk -- возвращает успешный Result с значением
@@ -27,16 +29,19 @@ func NewOk[T any](result T) Result[T] {
 	sf := Result[T]{
 		val: result,
 	}
+	runtime.SetFinalizer(&sf, sf.destroy)
 	return sf
 }
 
 // NewErr -- возвращает Result с ошибкой
 func NewErr[T any](err error) Result[T] {
 	Hassert(err != nil, "NewError(): err==nil")
-	return Result[T]{
+	sf := Result[T]{
 		err:   err,
 		isErr: true,
 	}
+	runtime.SetFinalizer(&sf, sf.destroy)
+	return sf
 }
 
 // WrapErr -- оборачивает существующий Result с ошибкой с новой ошибкой
@@ -45,17 +50,26 @@ func WrapErr[T any](res Result[T], err error) Result[T] {
 	Hassert(err != nil, "WrapErr(): err==nil")
 	err0 := res.Err()
 	err = fmt.Errorf("%v, err=\n\t%w", err0, err)
-	res0 := NewErr[T](err)
-	return res0
+	sf := NewErr[T](err)
+	runtime.SetFinalizer(&sf, sf.destroy)
+	return sf
+}
+
+func (sf *Result[T]) destroy(res *Result[T]) {
+	if !res.isCheck {
+		Hassert(false, "Result[T].destroy(): err not checked")
+	}
 }
 
 // IsOk -- возвращает true, если Result содержит значение
 func (sf *Result[T]) IsOk() bool {
+	sf.isCheck = true
 	return !sf.isErr
 }
 
 // IsErr -- возвращает true, если Result содержит ошибку
 func (sf *Result[T]) IsErr() bool {
+	sf.isCheck = true
 	return sf.isErr
 }
 
@@ -67,6 +81,7 @@ func (sf *Result[T]) Val() T {
 
 // ValOr -- возвращает значение, если оно есть, или значение по умолчанию
 func (sf *Result[T]) ValOr(defaultValue T) T {
+	sf.isCheck = true
 	if sf.isErr {
 		return defaultValue
 	}
@@ -76,6 +91,7 @@ func (sf *Result[T]) ValOr(defaultValue T) T {
 // ValOrFn -- возвращает значение, если оно есть, или результат выполнения функции
 func (sf *Result[T]) ValOrFn(fn func() T) T {
 	Hassert(fn != nil, "Result[T].ValOrFn(): fn==nil")
+	sf.isCheck = true
 	if sf.isErr {
 		return fn()
 	}
@@ -85,12 +101,14 @@ func (sf *Result[T]) ValOrFn(fn func() T) T {
 // Err -- возвращает ошибку, если она есть
 func (sf *Result[T]) Err() error {
 	Hassert(sf.isErr, "Result[T].Err(): err==nil")
+	sf.isCheck = true
 	return sf.err
 }
 
 // Error -- возвращает строковое представление ошибки, если она есть
 func (sf *Result[T]) Error() string {
 	Hassert(sf.isErr, "Result[T].Error(): err==nil")
+	sf.isCheck = true
 	return sf.err.Error()
 }
 
@@ -102,6 +120,7 @@ func (sf *Result[T]) Hassert(msgFormat string, args ...any) {
 	}
 	msg := fmt.Sprintf(msgFormat+strErr, args...)
 	Hassert(!sf.isErr, msg)
+	sf.isCheck = true
 }
 
 // Assert -- проверяет, что нет ошибки (с паникой только на локальном стенде)
@@ -112,4 +131,5 @@ func (sf *Result[T]) Assert(msgFormat string, args ...any) {
 	}
 	msg := fmt.Sprintf(msgFormat+strErr, args...)
 	Assert(!sf.isErr, msg)
+	sf.isCheck = true
 }

+ 23 - 0
v2/krn/ktypes/kresult_test.go

@@ -2,6 +2,7 @@ package ktypes
 
 import (
 	"fmt"
+	"runtime"
 	"testing"
 )
 
@@ -14,6 +15,28 @@ func TestResult(t *testing.T) {
 		t: t,
 	}
 	sf.create()
+	sf.notCheck()
+
+}
+
+// Создаёт новый результат и не проверяет его
+func (sf *testerResult) notCheck() {
+	sf.t.Log("notCheck")
+	defer func() {
+		if _panic := recover(); _panic == nil {
+			sf.t.Fatalf("notCheck(): panic==nil")
+		}
+	}()
+	res := NewOk("test")
+	{
+		val := res.Val()
+		if val != "test" {
+			sf.t.Fatalf("notCheck() test!=test")
+		}
+	}
+	runtime.Gosched()
+	runtime.GC()
+	res.destroy(&res)
 }
 
 // Создаёт новый Result