This is an automated email from the ASF dual-hosted git repository.

sjanc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newtmgr.git

commit 48a992209d0ade35b856bca12e6205ffbb364bad
Author: Szymon Janc <[email protected]>
AuthorDate: Thu Oct 22 00:25:31 2026 +0200

    nmxact/nmxutil: Add unit tests for pure helpers
    
    Cover Fragment for positive MTUs, NMP sequence and CoAP token
    wrap-around, SeqToToken, GetNextId, the CBOR encode/decode helpers
    (including invalid input), StopAndDrainTimer and the nmxerr error
    types with their Is*/To* helpers.
    
    Assisted-by: Claude:claude-opus-5-5
---
 nmxact/nmxutil/nmxerr_test.go  | 134 +++++++++++++++++++++++++++++
 nmxact/nmxutil/nmxutil_test.go | 185 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 319 insertions(+)

diff --git a/nmxact/nmxutil/nmxerr_test.go b/nmxact/nmxutil/nmxerr_test.go
new file mode 100644
index 0000000..c47d539
--- /dev/null
+++ b/nmxact/nmxutil/nmxerr_test.go
@@ -0,0 +1,134 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package nmxutil
+
+import (
+       "errors"
+       "testing"
+)
+
+func TestErrorPredicates(t *testing.T) {
+       type pred struct {
+               name string
+               fn   func(error) bool
+       }
+
+       preds := []pred{
+               {"IsRspTimeout", IsRspTimeout},
+               {"IsBleSesnDisconnect", IsBleSesnDisconnect},
+               {"IsSesnAlreadyOpen", IsSesnAlreadyOpen},
+               {"IsSesnClosed", IsSesnClosed},
+               {"IsScanTmo", IsScanTmo},
+               {"IsXport", IsXport},
+               {"IsBleHost", IsBleHost},
+               {"IsAlready", IsAlready},
+               {"IsBleSecurity", IsBleSecurity},
+       }
+
+       // errs[i] must satisfy exactly preds[i].
+       errs := []error{
+               FmtRspTimeoutError("tmo %d", 1),
+               NewBleSesnDisconnectError(0x13, "disc"),
+               NewSesnAlreadyOpenError("open"),
+               NewSesnClosedError("closed"),
+               NewScanTmoError("scan"),
+               NewXportError("xport"),
+               FmtBleHostError(5, "host %s", "err"),
+               NewAlreadyError("already"),
+               NewBleSecurityError("sec"),
+       }
+
+       for i, err := range errs {
+               for j, p := range preds {
+                       want := i == j
+                       if got := p.fn(err); got != want {
+                               t.Errorf("%s(%T) = %v, want %v", p.name, err, 
got, want)
+                       }
+               }
+       }
+
+       for _, p := range preds {
+               if p.fn(nil) {
+                       t.Errorf("%s(nil) = true", p.name)
+               }
+               if p.fn(errors.New("plain")) {
+                       t.Errorf("%s(plain error) = true", p.name)
+               }
+       }
+}
+
+func TestErrorText(t *testing.T) {
+       tests := []struct {
+               err  error
+               want string
+       }{
+               {FmtRspTimeoutError("a=%d b=%s", 1, "x"), "a=1 b=x"},
+               {NewBleSesnDisconnectError(1, "disc"), "disc"},
+               {NewSesnAlreadyOpenError("open"), "open"},
+               {NewSesnClosedError("closed"), "closed"},
+               {NewScanTmoError("scan"), "scan"},
+               {NewXportError("xport"), "xport"},
+               {FmtBleHostError(1, "%s-%d", "h", 2), "h-2"},
+               {NewAlreadyError("already"), "already"},
+               {NewBleSecurityError("sec"), "sec"},
+       }
+
+       for _, tt := range tests {
+               if got := tt.err.Error(); got != tt.want {
+                       t.Errorf("%T.Error() = %q, want %q", tt.err, got, 
tt.want)
+               }
+       }
+}
+
+func TestToBleHost(t *testing.T) {
+       e := NewBleHostError(0x205, "x")
+       if got := ToBleHost(e); got != e {
+               t.Fatalf("ToBleHost() = %v, want %v", got, e)
+       }
+       if got := ToBleHost(e); got.Status != 0x205 {
+               t.Fatalf("Status = %#x, want 0x205", got.Status)
+       }
+       if got := ToBleHost(NewXportError("x")); got != nil {
+               t.Fatalf("ToBleHost(xport) = %v, want nil", got)
+       }
+       if got := ToBleHost(nil); got != nil {
+               t.Fatalf("ToBleHost(nil) = %v, want nil", got)
+       }
+}
+
+func TestToBleSecurity(t *testing.T) {
+       e := NewBleSecurityError("x")
+       if got := ToBleSecurity(e); got != e {
+               t.Fatalf("ToBleSecurity() = %v, want %v", got, e)
+       }
+       if got := ToBleSecurity(NewBleHostError(1, "x")); got != nil {
+               t.Fatalf("ToBleSecurity(host) = %v, want nil", got)
+       }
+       if got := ToBleSecurity(nil); got != nil {
+               t.Fatalf("ToBleSecurity(nil) = %v, want nil", got)
+       }
+}
+
+func TestBleSesnDisconnectReason(t *testing.T) {
+       e := NewBleSesnDisconnectError(0x13, "disc")
+       if e.Reason != 0x13 {
+               t.Fatalf("Reason = %#x, want 0x13", e.Reason)
+       }
+}
diff --git a/nmxact/nmxutil/nmxutil_test.go b/nmxact/nmxutil/nmxutil_test.go
new file mode 100644
index 0000000..13967cd
--- /dev/null
+++ b/nmxact/nmxutil/nmxutil_test.go
@@ -0,0 +1,185 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package nmxutil
+
+import (
+       "bytes"
+       "reflect"
+       "testing"
+       "time"
+)
+
+func TestFragment(t *testing.T) {
+       data := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+
+       tests := []struct {
+               name string
+               b    []byte
+               mtu  int
+               want [][]byte
+       }{
+               {"empty", []byte{}, 4, [][]byte{}},
+               {"nil", nil, 4, [][]byte{}},
+               {"mtu 1", data[:3], 1, [][]byte{{0}, {1}, {2}}},
+               {"uneven", data, 4,
+                       [][]byte{{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9}}},
+               {"even", data, 5,
+                       [][]byte{{0, 1, 2, 3, 4}, {5, 6, 7, 8, 9}}},
+               {"exact", data, 10, [][]byte{data}},
+               {"larger mtu", data, 100, [][]byte{data}},
+       }
+
+       for _, tt := range tests {
+               got := Fragment(tt.b, tt.mtu)
+               if !reflect.DeepEqual(got, tt.want) {
+                       t.Errorf("%s: Fragment() = %v, want %v", tt.name, got, 
tt.want)
+               }
+
+               // Fragments always reassemble into the original buffer.
+               if j := bytes.Join(got, nil); !bytes.Equal(j, tt.b) {
+                       t.Errorf("%s: joined fragments = %v, want %v", tt.name, 
j, tt.b)
+               }
+       }
+}
+
+func TestNextNmpSeqWrap(t *testing.T) {
+       seqMutex.Lock()
+       nextNmpSeq = 0xfe
+       nmpSeqBeenRead = true
+       seqMutex.Unlock()
+
+       for _, want := range []uint8{0xfe, 0xff, 0x00, 0x01} {
+               if got := NextNmpSeq(); got != want {
+                       t.Fatalf("NextNmpSeq() = %#x, want %#x", got, want)
+               }
+       }
+}
+
+func TestNextTokenWrap(t *testing.T) {
+       seqMutex.Lock()
+       nextOicSeq = 0xff
+       oicSeqBeenRead = true
+       seqMutex.Unlock()
+
+       for _, want := range []uint8{0xff, 0x00, 0x01} {
+               got := NextToken()
+               if !bytes.Equal(got, []byte{want}) {
+                       t.Fatalf("NextToken() = % x, want %02x", got, want)
+               }
+       }
+}
+
+func TestSeqToToken(t *testing.T) {
+       for _, seq := range []uint8{0, 1, 0x7f, 0xff} {
+               if got := SeqToToken(seq); !bytes.Equal(got, []byte{seq}) {
+                       t.Errorf("SeqToToken(%d) = % x", seq, got)
+               }
+       }
+}
+
+func TestGetNextId(t *testing.T) {
+       a := GetNextId()
+       b := GetNextId()
+       if b != a+1 {
+               t.Fatalf("GetNextId() = %d after %d", b, a)
+       }
+}
+
+func TestCborMapRoundTrip(t *testing.T) {
+       in := map[string]interface{}{
+               "s": "str",
+               "b": []byte{1, 2},
+               "t": true,
+       }
+
+       b, err := EncodeCborMap(in)
+       if err != nil {
+               t.Fatalf("EncodeCborMap(): %v", err)
+       }
+
+       out, err := DecodeCborMap(b)
+       if err != nil {
+               t.Fatalf("DecodeCborMap(): %v", err)
+       }
+       if !reflect.DeepEqual(in, out) {
+               t.Fatalf("round trip = %#v, want %#v", out, in)
+       }
+}
+
+func TestCborRoundTrip(t *testing.T) {
+       in := []interface{}{"a", uint64(5), int64(-3)}
+
+       b, err := EncodeCbor(in)
+       if err != nil {
+               t.Fatalf("EncodeCbor(): %v", err)
+       }
+
+       out, err := DecodeCbor(b)
+       if err != nil {
+               t.Fatalf("DecodeCbor(): %v", err)
+       }
+       if !reflect.DeepEqual(in, out) {
+               t.Fatalf("round trip = %#v, want %#v", out, in)
+       }
+}
+
+func TestDecodeCborInvalid(t *testing.T) {
+       bad := [][]byte{
+               // Truncated map.
+               {0xa1, 0x61},
+               // Reserved additional info.
+               {0x1c},
+       }
+
+       for _, b := range bad {
+               if _, err := DecodeCbor(b); err == nil {
+                       t.Errorf("DecodeCbor(% x): expected error", b)
+               }
+               if _, err := DecodeCborMap(b); err == nil {
+                       t.Errorf("DecodeCborMap(% x): expected error", b)
+               }
+       }
+
+       // Valid CBOR, but not a map.
+       if _, err := DecodeCborMap([]byte{0x01}); err == nil {
+               t.Errorf("DecodeCborMap(int): expected error")
+       }
+}
+
+func TestStopAndDrainTimer(t *testing.T) {
+       // Timer still running.
+       tm := time.NewTimer(time.Hour)
+       StopAndDrainTimer(tm)
+
+       // Timer already expired but its value not received; must not block.
+       tm = time.NewTimer(time.Millisecond)
+       time.Sleep(20 * time.Millisecond)
+
+       done := make(chan struct{})
+       go func() {
+               StopAndDrainTimer(tm)
+               close(done)
+       }()
+       select {
+       case <-done:
+       case <-time.After(5 * time.Second):
+               t.Fatalf("StopAndDrainTimer blocked")
+       }
+}

Reply via email to