Repository: containerd/nerdctl

Issue:
nerdctl inspect should not return a stream of json array, but a single array
### Description

In a number of different occasions, but specifically when querying different objects in one call, nerdctl will return a stream of json arrays.

In comparison, docker returns one array.

### Steps to reproduce the issue

1. nerdctl inspect some_container some_image
2. docker inspect some_container some_image



### Describe the results you received and expected

na

### What version of nerdctl are you using?

1.7.6

### Are you using a variant of nerdctl? (e.g., Rancher Desktop)

None

### Host information

_No response_

Discussion:

Solution patch:
diff --git a/cmd/nerdctl/container/container_inspect.go b/cmd/nerdctl/container/container_inspect.go
index 8f1b6b80..78560b63 100644
--- a/cmd/nerdctl/container/container_inspect.go
+++ b/cmd/nerdctl/container/container_inspect.go
@@ -21,15 +21,18 @@ import (
 
 	"github.com/spf13/cobra"
 
+	"github.com/containerd/log"
+
 	"github.com/containerd/nerdctl/v2/cmd/nerdctl/completion"
 	"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
 	"github.com/containerd/nerdctl/v2/pkg/api/types"
 	"github.com/containerd/nerdctl/v2/pkg/clientutil"
 	"github.com/containerd/nerdctl/v2/pkg/cmd/container"
+	"github.com/containerd/nerdctl/v2/pkg/formatter"
 )
 
 func inspectCommand() *cobra.Command {
-	var cmd = &cobra.Command{
+	cmd := &cobra.Command{
 		Use:               "inspect [flags] CONTAINER [CONTAINER, ...]",
 		Short:             "Display detailed information on one or more containers.",
 		Long:              "Hint: set `--mode=native` for showing the full output",
@@ -100,7 +103,18 @@ func inspectAction(cmd *cobra.Command, args []string) error {
 	}
 	defer cancel()
 
-	return container.Inspect(ctx, client, args, opt)
+	entries, err := container.Inspect(ctx, client, args, opt)
+	if err != nil {
+		return err
+	}
+
+	// Display
+	if len(entries) > 0 {
+		if formatErr := formatter.FormatSlice(opt.Format, opt.Stdout, entries); formatErr != nil {
+			log.G(ctx).Error(formatErr)
+		}
+	}
+	return err
 }
 
 func containerInspectShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
diff --git a/cmd/nerdctl/image/image_inspect.go b/cmd/nerdctl/image/image_inspect.go
index 833b770f..5dd7238a 100644
--- a/cmd/nerdctl/image/image_inspect.go
+++ b/cmd/nerdctl/image/image_inspect.go
@@ -21,11 +21,14 @@ import (
 
 	"github.com/spf13/cobra"
 
+	"github.com/containerd/log"
+
 	"github.com/containerd/nerdctl/v2/cmd/nerdctl/completion"
 	"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
 	"github.com/containerd/nerdctl/v2/pkg/api/types"
 	"github.com/containerd/nerdctl/v2/pkg/clientutil"
 	"github.com/containerd/nerdctl/v2/pkg/cmd/image"
+	"github.com/containerd/nerdctl/v2/pkg/formatter"
 )
 
 func inspectCommand() *cobra.Command {
@@ -102,7 +105,18 @@ func imageInspectAction(cmd *cobra.Command, args []string) error {
 	}
 	defer cancel()
 
-	return image.Inspect(ctx, client, args, options)
+	entries, err := image.Inspect(ctx, client, args, options)
+	if err != nil {
+		return err
+	}
+
+	// Display
+	if len(entries) > 0 {
+		if formatErr := formatter.FormatSlice(options.Format, options.Stdout, entries); formatErr != nil {
+			log.G(ctx).Error(formatErr)
+		}
+	}
+	return err
 }
 
 func imageInspectShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
diff --git a/cmd/nerdctl/inspect/inspect.go b/cmd/nerdctl/inspect/inspect.go
index 8c62f54e..0473f1bd 100644
--- a/cmd/nerdctl/inspect/inspect.go
+++ b/cmd/nerdctl/inspect/inspect.go
@@ -22,6 +22,8 @@ import (
 
 	"github.com/spf13/cobra"
 
+	"github.com/containerd/log"
+
 	"github.com/containerd/nerdctl/v2/cmd/nerdctl/completion"
 	containercmd "github.com/containerd/nerdctl/v2/cmd/nerdctl/container"
 	"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
@@ -30,12 +32,13 @@ import (
 	"github.com/containerd/nerdctl/v2/pkg/clientutil"
 	"github.com/containerd/nerdctl/v2/pkg/cmd/container"
 	"github.com/containerd/nerdctl/v2/pkg/cmd/image"
+	"github.com/containerd/nerdctl/v2/pkg/formatter"
 	"github.com/containerd/nerdctl/v2/pkg/idutil/containerwalker"
 	"github.com/containerd/nerdctl/v2/pkg/idutil/imagewalker"
 )
 
 func Command() *cobra.Command {
-	var cmd = &cobra.Command{
+	cmd := &cobra.Command{
 		Use:               "inspect",
 		Short:             "Return low-level information on objects.",
 		Args:              cobra.MinimumNArgs(1),
@@ -79,6 +82,10 @@ func inspectAction(cmd *cobra.Command, args []string) error {
 	}
 	namespace := globalOptions.Namespace
 	address := globalOptions.Address
+	format, err := cmd.Flags().GetString("format")
+	if err != nil {
+		return err
+	}
 	inspectType, err := cmd.Flags().GetString("type")
 	if err != nil {
 		return err
@@ -130,6 +137,7 @@ func inspectAction(cmd *cobra.Command, args []string) error {
 	}
 
 	var errs []error
+	var entries []interface{}
 	for _, req := range args {
 		var ni int
 		var nc int
@@ -150,12 +158,16 @@ func inspectAction(cmd *cobra.Command, args []string) error {
 		if ni == 0 && nc == 0 {
 			errs = append(errs, fmt.Errorf("no such object %s", req))
 		} else if ni > 0 {
-			if err := image.Inspect(ctx, client, []string{req}, imageInspectOptions); err != nil {
+			if imageEntries, err := image.Inspect(ctx, client, []string{req}, imageInspectOptions); err != nil {
 				errs = append(errs, err)
+			} else {
+				entries = append(entries, imageEntries...)
 			}
 		} else if nc > 0 {
-			if err := container.Inspect(ctx, client, []string{req}, containerInspectOptions); err != nil {
+			if containerEntries, err := container.Inspect(ctx, client, []string{req}, containerInspectOptions); err != nil {
 				errs = append(errs, err)
+			} else {
+				entries = append(entries, containerEntries...)
 			}
 		}
 	}
@@ -164,6 +176,10 @@ func inspectAction(cmd *cobra.Command, args []string) error {
 		return fmt.Errorf("%d errors: %v", len(errs), errs)
 	}
 
+	if formatErr := formatter.FormatSlice(format, cmd.OutOrStdout(), entries); formatErr != nil {
+		log.G(ctx).Error(formatErr)
+	}
+
 	return nil
 }
 
diff --git a/mod/tigron/.golangci.yml b/mod/tigron/.golangci.yml
index 22d2a86d..ab1817e4 100644
--- a/mod/tigron/.golangci.yml
+++ b/mod/tigron/.golangci.yml
@@ -37,6 +37,7 @@ linters:
     - cyclop            # provided by revive
     - exhaustruct       # does not serve much of a purpose
     - errcheck          # provided by revive
+    - errchkjson        # forces handling of json err (eg: prevents _), which is too much
     - forcetypeassert   # provided by revive
     - funlen            # provided by revive
     - gocognit          # provided by revive
@@ -65,7 +66,7 @@ linters:
           arguments: [60]
         - name: function-length
           # Default is 50, 75
-          arguments: [80, 180]
+          arguments: [80, 200]
         - name: cyclomatic
           # Default is 10
           arguments: [30]
@@ -93,11 +94,17 @@ linters:
           files:
             - $all
           allow:
+            # Allowing go standard library and tigron itself
             - $gostd
             - github.com/containerd/nerdctl/mod/tigron
+            # We use creack as our base for pty
             - github.com/creack/pty
+            # Used for display width computation in internal/formatter
+            - golang.org/x/text/width
+            # errgroups and term (make raw) are used by internal/pipes
             - golang.org/x/sync
             - golang.org/x/term
+            # EXPERIMENTAL: for go routines leakage detection
             - go.uber.org/goleak
     staticcheck:
       checks:
diff --git a/mod/tigron/expect/comparators.go b/mod/tigron/expect/comparators.go
index 36b09de5..7bf87803 100644
--- a/mod/tigron/expect/comparators.go
+++ b/mod/tigron/expect/comparators.go
@@ -19,20 +19,22 @@
 package expect
 
 import (
+	"encoding/json"
 	"regexp"
 	"testing"
 
 	"github.com/containerd/nerdctl/mod/tigron/internal/assertive"
 	"github.com/containerd/nerdctl/mod/tigron/test"
+	"github.com/containerd/nerdctl/mod/tigron/tig"
 )
 
 // All can be used as a parameter for expected.Output to group a set of comparators.
 func All(comparators ...test.Comparator) test.Comparator {
-	return func(stdout, info string, t *testing.T) {
+	return func(stdout, _ string, t *testing.T) {
 		t.Helper()
 
 		for _, comparator := range comparators {
-			comparator(stdout, info, t)
+			comparator(stdout, "", t)
 		}
 	}
 }
@@ -40,32 +42,47 @@ func All(comparators ...test.Comparator) test.Comparator {
 // Contains can be used as a parameter for expected.Output and ensures a comparison string is found contained in the
 // output.
 func Contains(compare string) test.Comparator {
-	return func(stdout, info string, t *testing.T) {
+	return func(stdout, _ string, t *testing.T) {
 		t.Helper()
-		assertive.Contains(assertive.WithFailLater(t), stdout, compare, info)
+		assertive.Contains(assertive.WithFailLater(t), stdout, compare, "Inspecting output (contains)")
 	}
 }
 
 // DoesNotContain is to be used for expected.Output to ensure a comparison string is NOT found in the output.
 func DoesNotContain(compare string) test.Comparator {
-	return func(stdout, info string, t *testing.T) {
+	return func(stdout, _ string, t *testing.T) {
 		t.Helper()
-		assertive.DoesNotContain(assertive.WithFailLater(t), stdout, compare, info)
+		assertive.DoesNotContain(assertive.WithFailLater(t), stdout, compare, "Inspecting output (does not contain)")
 	}
 }
 
 // Equals is to be used for expected.Output to ensure it is exactly the output.
 func Equals(compare string) test.Comparator {
-	return func(stdout, info string, t *testing.T) {
+	return func(stdout, _ string, t *testing.T) {
 		t.Helper()
-		assertive.IsEqual(assertive.WithFailLater(t), stdout, compare, info)
+		assertive.IsEqual(assertive.WithFailLater(t), stdout, compare, "Inspecting output (equals)")
 	}
 }
 
 // Match is to be used for expected.Output to ensure we match a regexp.
 func Match(reg *regexp.Regexp) test.Comparator {
-	return func(stdout, info string, t *testing.T) {
+	return func(stdout, _ string, t *testing.T) {
 		t.Helper()
-		assertive.Match(assertive.WithFailLater(t), stdout, reg, info)
+		assertive.Match(assertive.WithFailLater(t), stdout, reg, "Inspecting output (match)")
+	}
+}
+
+// JSON allows to verify that the output can be marshalled into T, and optionally can be further verified by a provided
+// method.
+func JSON[T any](obj T, verifier func(T, string, tig.T)) test.Comparator {
+	return func(stdout, _ string, t *testing.T) {
+		t.Helper()
+
+		err := json.Unmarshal([]byte(stdout), &obj)
+		assertive.ErrorIsNil(assertive.WithSilentSuccess(t), err, "Unmarshalling JSON from stdout must succeed")
+
+		if verifier != nil && err == nil {
+			verifier(obj, "Inspecting output (JSON)", t)
+		}
 	}
 }
diff --git a/mod/tigron/expect/doc.md b/mod/tigron/expect/doc.md
new file mode 100644
index 00000000..48b8d4be
--- /dev/null
+++ b/mod/tigron/expect/doc.md
@@ -0,0 +1,221 @@
+# Expectations
+
+Attaching expectations to a test case is how the developer can express conditions on exit code, stdout, or stderr,
+to be verified for the test to pass.
+
+The simplest way to do that is to use the helper `test.Expects(exitCode int, errors []error, outputCompare test.Comparator)`.
+
+```go
+package main
+
+import (
+    "testing"
+
+    "github.com/containerd/nerdctl/mod/tigron/test"
+)
+
+func TestMyThing(t *testing.T) {
+    // Declare your test
+    myTest := &test.Case{}
+
+    // Attach a command to run
+    myTest.Command = test.Custom("ls")
+
+    // Set your expectations
+    myTest.Expected = test.Expects(expect.ExitCodeSuccess, nil, nil)
+
+    // Run it
+    myTest.Run(t)
+}
+```
+
+### Exit status expectations
+
+The first parameter, `exitCode` should be set to one of the provided `expect.ExitCodeXXX` constants:
+- `expect.ExitCodeSuccess`: validates that the command ran and exited successfully
+- `expect.ExitCodeTimeout`: validates that the command did time out
+- `expect.ExitCodeSignaled`: validates that the command received a signal
+- `expect.ExitCodeGenericFail`: validates that the command failed (failed to start, or returned a non-zero exit code)
+- `expect.ExitCodeNoCheck`: does not enforce any verification at all on the command
+
+... you may also pass explicit exit codes directly (> 0) if you want to precisely match them.
+
+### Stderr expectations with []error
+
+To validate that stderr contain specific information, you can pass a slice of `error` as `test.Expects`
+second parameter.
+
+The command output on stderr is then verified to contain all stringified errors.
+
+### Stdout expectations with Comparators
+
+The last parameter of `test.Expects` accepts a `test.Comparator`, which allows testing the content of the command
+output on `stdout`.
+
+The following ready-made `test.Comparator` generators are provided:
+- `expect.Contains(string)`: verifies that stdout contains the string parameter
+- `expect.DoesNotContain(string)`: negation of above
+- `expect.Equals(string)`: strict equality
+- `expect.Match(*regexp.Regexp)`: regexp matching
+- `expect.All(comparators ...Comparator)`: allows to bundle together a bunch of other comparators
+- `expect.JSON[T any](obj T, verifier func(T, string, tig.T))`: allows to verify the output is valid JSON and optionally
+pass `verifier(T, string, tig.T)` extra validation
+
+### A complete example
+
+```go
+package main
+
+import (
+    "testing"
+    "errors"
+
+    "github.com/containerd/nerdctl/mod/tigron/tig"
+    "github.com/containerd/nerdctl/mod/tigron/test"
+    "github.com/containerd/nerdctl/mod/tigron/expect"
+)
+
+type Thing struct {
+    Name string
+}
+
+func TestMyThing(t *testing.T) {
+    // Declare your test
+    myTest := &test.Case{}
+
+    // Attach a command to run
+    myTest.Command = test.Custom("bash", "-c", "--", ">&2 echo thing; echo '{\"Name\": \"out\"}'; exit 42;")
+
+    // Set your expectations
+    myTest.Expected = test.Expects(
+        expect.ExitCodeGenericFail,
+        []error{errors.New("thing")},
+        expect.All(
+            expect.Contains("out"),
+            expect.DoesNotContain("something"),
+            expect.JSON(&Thing{}, func(obj *Thing, info string, t tig.T) {
+                assert.Equal(t, obj.Name, "something", info)
+            }),
+        ),
+    )
+
+    // Run it
+    myTest.Run(t)
+}
+```
+
+### Custom stdout comparators
+
+If you need to implement more advanced verifications on stdout that the ready-made comparators can't do,
+you can implement your own custom `test.Comparator`.
+
+For example:
+
+```go
+package whatever
+
+import (
+    "testing"
+
+    "gotest.tools/v3/assert"
+
+    "github.com/containerd/nerdctl/mod/tigron/tig"
+    "github.com/containerd/nerdctl/mod/tigron/test"
+)
+
+func TestMyThing(t *testing.T) {
+    // Declare your test
+    myTest := &test.Case{}
+
+    // Attach a command to run
+    myTest.Command = test.Custom("ls")
+
+    // Set your expectations
+    myTest.Expected = test.Expects(0, nil, func(stdout, info string, t tig.T){
+        t.Helper()
+        // Bla bla, do whatever advanced stuff and some asserts
+    })
+
+    // Run it
+    myTest.Run(t)
+}
+
+// You can of course generalize your comparator into a generator if it is going to be useful repeatedly
+
+func MyComparatorGenerator(param1, param2 any) test.Comparator {
+    return func(stdout, info string, t tig.T) {
+        t.Helper()
+        // Do your thing...
+        // ...
+    }
+}
+
+```
+
+You can now pass along `MyComparator(comparisonString)` as the third parameter of `test.Expects`, or compose it with
+other comparators using `expect.All(MyComparator(comparisonString), OtherComparator(somethingElse))`
+
+Note that you have access to an opaque `info` string, that provides a brief formatted header message that assert
+will use in case of failure to provide context on the error.
+You may of course ignore it and write your own message.
+
+### Advanced expectations
+
+You may want to have expectations that contain a certain piece of data that is being used in the command or at
+other stages of your test (like `Setup` for example).
+
+To achieve that, you should write your own `test.Manager` instead of using the helper `test.Expects`.
+
+A manager is a simple function which only role is to return a `test.Expected` struct.
+The `test.Manager` signature makes available `test.Data` and `test.Helpers` to you.
+
+Here is an example, where we are using `data.Get("sometestdata")`.
+
+```go
+package main
+
+import (
+    "errors"
+    "testing"
+
+    "gotest.tools/v3/assert"
+
+    "github.com/containerd/nerdctl/mod/tigron/test"
+)
+
+func TestMyThing(t *testing.T) {
+    // Declare your test
+    myTest := &test.Case{}
+
+    myTest.Setup = func(data test.Data, helpers test.Helpers){
+        // Do things...
+        // ...
+        // Save this for later
+        data.Set("something", "lalala")
+    }
+
+    // Attach a command to run
+    myTest.Command = test.Custom("somecommand")
+
+    // Set your fully custom expectations
+    myTest.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
+        // With a custom Manager you have access to both the test.Data and test.Helpers to perform more
+        // refined verifications.
+        return &test.Expected{
+            ExitCode: 1,
+            Errors: []error{
+                errors.New("foobla"),
+            },
+            Output: func(stdout, info string, t tig.T) {
+                t.Helper()
+
+                // Retrieve the data that was set during the Setup phase.
+                assert.Assert(t, stdout == data.Get("sometestdata"), info)
+            },
+        }
+    }
+
+    // Run it
+    myTest.Run(t)
+}
+```
diff --git a/mod/tigron/go.mod b/mod/tigron/go.mod
index 6979d174..7d355696 100644
--- a/mod/tigron/go.mod
+++ b/mod/tigron/go.mod
@@ -5,8 +5,9 @@ go 1.23.0
 require (
 	github.com/creack/pty v1.1.24
 	go.uber.org/goleak v1.3.0
-	golang.org/x/sync v0.12.0
+	golang.org/x/sync v0.13.0
 	golang.org/x/term v0.30.0
+	golang.org/x/text v0.24.0
 )
 
 require golang.org/x/sys v0.31.0 // indirect
diff --git a/mod/tigron/go.sum b/mod/tigron/go.sum
index b6ba6f6b..fdf139e7 100644
--- a/mod/tigron/go.sum
+++ b/mod/tigron/go.sum
@@ -8,11 +8,13 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
-golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
+golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
 golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
 golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
+golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/mod/tigron/internal/assertive/assertive.go b/mod/tigron/internal/assertive/assertive.go
index 735c58c5..bc9b2df4 100644
--- a/mod/tigron/internal/assertive/assertive.go
+++ b/mod/tigron/internal/assertive/assertive.go
@@ -31,12 +31,12 @@ import (
 	"github.com/containerd/nerdctl/mod/tigron/tig"
 )
 
-// TODO: once debugging output will be cleaned-up, reintroduce hexdump.
-
 const (
+	markLineLength           = 20
 	expectedSuccessDecorator = "✅️ does verify:\t\t"
-	expectedFailDecorator    = "❌ does not verify:\t"
+	expectedFailDecorator    = "❌ FAILED!\t\t"
 	receivedDecorator        = "👀 testing:\t\t"
+	annotationDecorator      = "🖊️"
 	hyperlinkDecorator       = "🔗"
 )
 
@@ -163,8 +163,8 @@ func True(testing tig.T, comp bool, msg ...string) bool {
 
 // WithFailLater will allow an assertion to not fail the test immediately.
 // Failing later is necessary when asserting inside go routines, and also if you want many
-// successive asserts to all
-// evaluate instead of stopping at the first failing one.
+// successive asserts to all evaluate instead of stopping at the first failing one.
+// FIXME: it should be possible to have both WithFailLater and WithSilentSuccess at the same time.
 func WithFailLater(t tig.T) tig.T {
 	return &failLater{
 		t,
@@ -205,49 +205,76 @@ func evaluate(testing tig.T, isSuccess bool, actual, expected any, msg ...string
 func decorate(testing tig.T, isSuccess bool, actual, expected any, msg ...string) {
 	testing.Helper()
 
-	header := "\t"
+	if _, ok := testing.(*silentSuccess); !isSuccess || !ok {
+		head := strings.Repeat("<", markLineLength)
+		footer := strings.Repeat(">", markLineLength)
+		header := "\t"
 
-	hyperlink := getTopFrameFile()
-	if hyperlink != "" {
-		msg = append([]string{hyperlink + "\n"}, msg...)
-	}
+		custom := fmt.Sprintf("\t%s %s", annotationDecorator, strings.Join(msg, "\n"))
 
-	msg = append(msg, fmt.Sprintf("\t%s`%v`", receivedDecorator, actual))
+		msg = append([]string{"", head}, custom)
 
-	if isSuccess {
-		msg = append(msg,
-			fmt.Sprintf("\t%s%v", expectedSuccessDecorator, expected),
-		)
-	} else {
-		msg = append(msg,
-			fmt.Sprintf("\t%s%v", expectedFailDecorator, expected),
-		)
-	}
+		msg = append([]string{getTopFrameFile()}, msg...)
 
-	if _, ok := testing.(*silentSuccess); !isSuccess || !ok {
-		testing.Log(header + strings.Join(msg, "\n") + "\n")
+		msg = append(msg, fmt.Sprintf("\t%s`%v`", receivedDecorator, actual))
+
+		if isSuccess {
+			msg = append(msg,
+				fmt.Sprintf("\t%s%v", expectedSuccessDecorator, expected),
+			)
+		} else {
+			msg = append(msg,
+				fmt.Sprintf("\t%s%v", expectedFailDecorator, expected),
+			)
+		}
+
+		testing.Log(header + strings.Join(msg, "\n") + "\n" + footer + "\n")
 	}
 }
 
+// XXX FIXME #expert
+// Because of how golang testing works, the upper frame is the one from where t.Run is being called,
+// as (presumably) the passed function is starting with its own stack in a go routine.
+// In the case of subtests, t.Run being called from inside Tigron will make it so that the top frame
+// is case.go around line 233 (where we call Command.Run(), which is the one calling assertive).
+// To possibly address this:
+// plan a. just drop entirely OSC8 links and source extracts and trash all of this
+// plan b. get the top frame from the root test, and pass it to subtests on a custom property, the somehow into here
+// plan c. figure out a hack to call t.Run from the test file without ruining the Tigron UX
+// Dereference t.Run? Return a closure to be called from the top? w/o disabling inlining in the right place?
+// Short term, blacklisting /tigron (and /nerdtest) will at least prevent the wrong links from appearing in the output.
 func getTopFrameFile() string {
-	// Get the frames.
+	// Get the frames. Skip the first two frames - current one and caller.
 	//nolint:mnd // Whatever mnd...
-	pc := make([]uintptr, 20)
+	pc := make([]uintptr, 40)
 	//nolint:mnd // Whatever mnd...
 	n := runtime.Callers(2, pc)
 	callersFrames := runtime.CallersFrames(pc[:n])
 
-	var file string
+	var (
+		file       string
+		lineNumber int
+		frame      runtime.Frame
+	)
 
-	var lineNumber int
+	more := true
+	for more {
+		frame, more = callersFrames.Next()
 
-	var frame runtime.Frame
-	for range 20 {
-		frame, _ = callersFrames.Next()
+		// Once we are in the go main stack, bail out
 		if !strings.Contains(frame.Function, "/") {
 			break
 		}
 
+		// XXX see note above
+		if strings.Contains(frame.File, "/tigron") {
+			continue
+		}
+
+		if strings.Contains(frame.File, "/nerdtest") {
+			continue
+		}
+
 		file = frame.File
 		lineNumber = frame.Line
 	}
@@ -282,6 +309,6 @@ func getTopFrameFile() string {
 	return hyperlinkDecorator + " " + (&formatter.OSC8{
 		Text:     line,
 		Location: "file://" + file,
-		Line:     frame.Line,
+		Line:     lineNumber,
 	}).String()
 }
diff --git a/mod/tigron/internal/formatter/formatter.go b/mod/tigron/internal/formatter/formatter.go
index 765a3be5..43ce9fe5 100644
--- a/mod/tigron/internal/formatter/formatter.go
+++ b/mod/tigron/internal/formatter/formatter.go
@@ -19,77 +19,99 @@ package formatter
 import (
 	"fmt"
 	"strings"
-	"unicode/utf8"
+
+	"golang.org/x/text/width"
 )
 
 const (
 	maxLineLength = 110
 	maxLines      = 100
 	kMaxLength    = 7
+	spacer        = " "
 )
 
-func chunk(s string, length int) []string {
-	var chunks []string
-
-	lines := strings.Split(s, "\n")
-
-	for x := 0; x < maxLines && x < len(lines); x++ {
-		line := lines[x]
-		if utf8.RuneCountInString(line) < length {
-			chunks = append(chunks, line)
-
-			continue
-		}
-
-		for index := 0; index < utf8.RuneCountInString(line); index += length {
-			end := index + length
-			if end > utf8.RuneCountInString(line) {
-				end = utf8.RuneCountInString(line)
-			}
-
-			chunks = append(chunks, string([]rune(line)[index:end]))
-		}
-	}
-
-	if len(chunks) == maxLines {
-		chunks = append(chunks, "...")
-	}
-
-	return chunks
-}
-
-// Table formats a `n x 2` dataset into a series of rows.
-// FIXME: the problem with full-width emoji is that they are going to eff-up the maths and display
-// here...
-// Maybe the csv writer could be cheat-used to get the right widths.
+// Table formats a `n x 2` dataset into a series of n rows by 2 columns.
 //
 //nolint:mnd // Too annoying
-func Table(data [][]any) string {
+func Table(data [][]any, mark string) string {
 	var output string
 
 	for _, row := range data {
 		key := fmt.Sprintf("%v", row[0])
 		value := strings.ReplaceAll(fmt.Sprintf("%v", row[1]), "\t", "  ")
 
-		output += fmt.Sprintf("+%s+\n", strings.Repeat("-", maxLineLength-2))
-
-		if utf8.RuneCountInString(key) > kMaxLength {
-			key = string([]rune(key)[:kMaxLength-3]) + "..."
-		}
+		output += fmt.Sprintf("+%s+\n", strings.Repeat(mark, maxLineLength-2))
 
-		for _, line := range chunk(value, maxLineLength-kMaxLength-7) {
+		for _, line := range chunk(value, maxLineLength-kMaxLength-7, maxLines) {
 			output += fmt.Sprintf(
-				"| %-*s | %-*s |\n",
-				kMaxLength,
-				key,
-				maxLineLength-kMaxLength-7,
+				"| %s | %s |\n",
+				// Keys longer than one line of kMaxLength will be striped to one line
+				chunk(key, kMaxLength, 1)[0],
 				line,
 			)
 			key = ""
 		}
 	}
 
-	output += fmt.Sprintf("+%s+", strings.Repeat("-", maxLineLength-2))
+	output += fmt.Sprintf("+%s+", strings.Repeat(mark, maxLineLength-2))
 
 	return output
 }
+
+// chunk does take a string and split it in lines of maxLength size, accounting for characters display width.
+func chunk(s string, maxLength, maxLines int) []string {
+	chunks := []string{}
+
+	runes := []rune(s)
+
+	size := 0
+	start := 0
+
+	for index := range runes {
+		var segment string
+
+		switch width.LookupRune(runes[index]).Kind() {
+		case width.EastAsianWide, width.EastAsianFullwidth:
+			size += 2
+		case width.EastAsianAmbiguous, width.Neutral, width.EastAsianHalfwidth, width.EastAsianNarrow:
+			size++
+		default:
+			size++
+		}
+
+		switch {
+		case runes[index] == '\n':
+			// Met a line-break. Pad to size (removing the line break)
+			segment = string(runes[start:index])
+			segment += strings.Repeat(spacer, maxLength-size+1)
+			start = index + 1
+			size = 0
+		case size == maxLength:
+			// Line is full. Add the segment.
+			segment = string(runes[start : index+1])
+			start = index + 1
+			size = 0
+		case size > maxLength:
+			// Last char was double width. Push it back to next line, and pad with a single space.
+			segment = string(runes[start:index]) + spacer
+			start = index
+			size = 2
+		case index == len(runes)-1:
+			// End of string. Pad it to size.
+			segment = string(runes[start : index+1])
+			segment += strings.Repeat(spacer, maxLength-size)
+		default:
+			continue
+		}
+
+		chunks = append(chunks, segment)
+	}
+
+	if len(chunks) > maxLines {
+		chunks = append(chunks[0:maxLines], "...")
+	} else if len(chunks) == 0 {
+		chunks = []string{strings.Repeat(spacer, maxLength)}
+	}
+
+	return chunks
+}
diff --git a/mod/tigron/internal/mimicry/print.go b/mod/tigron/internal/mimicry/print.go
index 96c333c6..2f1c357c 100644
--- a/mod/tigron/internal/mimicry/print.go
+++ b/mod/tigron/internal/mimicry/print.go
@@ -40,7 +40,7 @@ func PrintCall(call *Call) string {
 	}
 
 	output := []string{
-		formatter.Table(debug),
+		formatter.Table(debug, "-"),
 		sectionSeparator,
 	}
 
diff --git a/pkg/cmd/container/inspect.go b/pkg/cmd/container/inspect.go
index 72db92d4..63c359ae 100644
--- a/pkg/cmd/container/inspect.go
+++ b/pkg/cmd/container/inspect.go
@@ -23,19 +23,17 @@ import (
 
 	containerd "github.com/containerd/containerd/v2/client"
 	"github.com/containerd/containerd/v2/core/snapshots"
-	"github.com/containerd/log"
 
 	"github.com/containerd/nerdctl/v2/pkg/api/types"
 	"github.com/containerd/nerdctl/v2/pkg/containerdutil"
 	"github.com/containerd/nerdctl/v2/pkg/containerinspector"
-	"github.com/containerd/nerdctl/v2/pkg/formatter"
 	"github.com/containerd/nerdctl/v2/pkg/idutil/containerwalker"
 	"github.com/containerd/nerdctl/v2/pkg/imgutil"
 	"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
 )
 
 // Inspect prints detailed information for each container in `containers`.
-func Inspect(ctx context.Context, client *containerd.Client, containers []string, options types.ContainerInspectOptions) error {
+func Inspect(ctx context.Context, client *containerd.Client, containers []string, options types.ContainerInspectOptions) ([]any, error) {
 	f := &containerInspector{
 		mode:        options.Mode,
 		size:        options.Size,
@@ -48,13 +46,11 @@ func Inspect(ctx context.Context, client *containerd.Client, containers []string
 	}
 
 	err := walker.WalkAll(ctx, containers, true)
-	if len(f.entries) > 0 {
-		if formatErr := formatter.FormatSlice(options.Format, options.Stdout, f.entries); formatErr != nil {
-			log.L.Error(formatErr)
-		}
+	if err != nil {
+		return []any{}, err
 	}
 
-	return err
+	return f.entries, nil
 }
 
 type containerInspector struct {
diff --git a/pkg/cmd/image/inspect.go b/pkg/cmd/image/inspect.go
index 35d3211f..3d549a4b 100644
--- a/pkg/cmd/image/inspect.go
+++ b/pkg/cmd/image/inspect.go
@@ -30,7 +30,6 @@ import (
 
 	"github.com/containerd/nerdctl/v2/pkg/api/types"
 	"github.com/containerd/nerdctl/v2/pkg/containerdutil"
-	"github.com/containerd/nerdctl/v2/pkg/formatter"
 	"github.com/containerd/nerdctl/v2/pkg/imageinspector"
 	"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
 	"github.com/containerd/nerdctl/v2/pkg/referenceutil"
@@ -87,7 +86,7 @@ func inspectIdentifier(ctx context.Context, client *containerd.Client, identifie
 }
 
 // Inspect prints detailed information of each image in `images`.
-func Inspect(ctx context.Context, client *containerd.Client, identifiers []string, options types.ImageInspectOptions) error {
+func Inspect(ctx context.Context, client *containerd.Client, identifiers []string, options types.ImageInspectOptions) ([]any, error) {
 	// Set a timeout
 	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
 	defer cancel()
@@ -189,16 +188,9 @@ func Inspect(ctx context.Context, client *containerd.Client, identifiers []strin
 		}
 	}
 
-	// Display
-	if len(entries) > 0 {
-		if formatErr := formatter.FormatSlice(options.Format, options.Stdout, entries); formatErr != nil {
-			log.G(ctx).Error(formatErr)
-		}
-	}
-
 	if len(errs) > 0 {
-		return fmt.Errorf("%d errors:\n%w", len(errs), errors.Join(errs...))
+		return []any{}, fmt.Errorf("%d errors:\n%w", len(errs), errors.Join(errs...))
 	}
 
-	return nil
+	return entries, nil
 }



Test patch:
diff --git a/cmd/nerdctl/inspect/inspect_test.go b/cmd/nerdctl/inspect/inspect_test.go
index 18b6d6ff..954b0e73 100644
--- a/cmd/nerdctl/inspect/inspect_test.go
+++ b/cmd/nerdctl/inspect/inspect_test.go
@@ -17,11 +17,60 @@
 package inspect
 
 import (
+	"encoding/json"
 	"testing"
 
+	"gotest.tools/v3/assert"
+
+	"github.com/containerd/nerdctl/mod/tigron/test"
+
+	"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
 	"github.com/containerd/nerdctl/v2/pkg/testutil"
+	"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
 )
 
 func TestMain(m *testing.M) {
 	testutil.M(m)
 }
+
+func TestInspectSimpleCase(t *testing.T) {
+	nerdtest.Setup()
+	testCase := &test.Case{
+		Description: "inspect container and image return one single json array",
+		Setup: func(data test.Data, helpers test.Helpers) {
+			identifier := data.Identifier()
+			helpers.Ensure("run", "-d", "--quiet", "--name", identifier, testutil.CommonImage, "sleep", nerdtest.Infinity)
+		},
+		Cleanup: func(data test.Data, helpers test.Helpers) {
+			identifier := data.Identifier()
+			helpers.Anyhow("rm", "-f", identifier)
+		},
+		Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
+			return helpers.Command("inspect", testutil.CommonImage, data.Identifier())
+		},
+		Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
+			return &test.Expected{
+				Output: func(stdout string, info string, t *testing.T) {
+					var inspectResult []json.RawMessage
+					err := json.Unmarshal([]byte(stdout), &inspectResult)
+					assert.NilError(t, err, "Unable to unmarshal output\n"+info)
+					assert.Equal(t, len(inspectResult), 2, "Unexpectedly got multiple results\n"+info)
+
+					var dci dockercompat.Image
+					err = json.Unmarshal(inspectResult[0], &dci)
+					assert.NilError(t, err, "Unable to unmarshal output\n"+info)
+					inspecti := nerdtest.InspectImage(helpers, testutil.CommonImage)
+					assert.Equal(t, dci.ID, inspecti.ID, info)
+
+					var dcc dockercompat.Container
+					err = json.Unmarshal(inspectResult[1], &dcc)
+					assert.NilError(t, err, "Unable to unmarshal output\n"+info)
+					inspectc := nerdtest.InspectContainer(helpers, data.Identifier())
+					assert.Assert(t, dcc.ID == inspectc.ID, info)
+				},
+			}
+		},
+	}
+
+	testCase.Run(t)
+}
diff --git a/cmd/nerdctl/volume/volume_inspect_test.go b/cmd/nerdctl/volume/volume_inspect_test.go
index 7e627a11..86d07006 100644
--- a/cmd/nerdctl/volume/volume_inspect_test.go
+++ b/cmd/nerdctl/volume/volume_inspect_test.go
@@ -18,7 +18,6 @@ package volume
 
 import (
 	"crypto/rand"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"os"
@@ -31,6 +30,7 @@ import (
 	"github.com/containerd/nerdctl/mod/tigron/expect"
 	"github.com/containerd/nerdctl/mod/tigron/require"
 	"github.com/containerd/nerdctl/mod/tigron/test"
+	"github.com/containerd/nerdctl/mod/tigron/tig"
 
 	"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
 	"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
@@ -99,15 +99,11 @@ func TestVolumeInspect(t *testing.T) {
 				return &test.Expected{
 					Output: expect.All(
 						expect.Contains(data.Get("vol1")),
-						func(stdout string, info string, t *testing.T) {
-							var dc []native.Volume
-							if err := json.Unmarshal([]byte(stdout), &dc); err != nil {
-								t.Fatal(err)
-							}
+						expect.JSON([]native.Volume{}, func(dc []native.Volume, info string, t tig.T) {
 							assert.Assert(t, len(dc) == 1, fmt.Sprintf("one result, not %d", len(dc))+info)
 							assert.Assert(t, dc[0].Name == data.Get("vol1"), fmt.Sprintf("expected name to be %q (was %q)", data.Get("vol1"), dc[0].Name)+info)
 							assert.Assert(t, dc[0].Labels == nil, fmt.Sprintf("expected labels to be nil and were %v", dc[0].Labels)+info)
-						},
+						}),
 					),
 				}
 			},
@@ -121,16 +117,12 @@ func TestVolumeInspect(t *testing.T) {
 				return &test.Expected{
 					Output: expect.All(
 						expect.Contains(data.Get("vol2")),
-						func(stdout string, info string, t *testing.T) {
-							var dc []native.Volume
-							if err := json.Unmarshal([]byte(stdout), &dc); err != nil {
-								t.Fatal(err)
-							}
+						expect.JSON([]native.Volume{}, func(dc []native.Volume, info string, t tig.T) {
 							labels := *dc[0].Labels
 							assert.Assert(t, len(labels) == 2, fmt.Sprintf("two results, not %d", len(labels)))
 							assert.Assert(t, labels["foo"] == "fooval", fmt.Sprintf("label foo should be fooval, not %s", labels["foo"]))
 							assert.Assert(t, labels["bar"] == "barval", fmt.Sprintf("label bar should be barval, not %s", labels["bar"]))
-						},
+						}),
 					),
 				}
 			},
@@ -145,13 +137,9 @@ func TestVolumeInspect(t *testing.T) {
 				return &test.Expected{
 					Output: expect.All(
 						expect.Contains(data.Get("vol1")),
-						func(stdout string, info string, t *testing.T) {
-							var dc []native.Volume
-							if err := json.Unmarshal([]byte(stdout), &dc); err != nil {
-								t.Fatal(err)
-							}
+						expect.JSON([]native.Volume{}, func(dc []native.Volume, info string, t tig.T) {
 							assert.Assert(t, dc[0].Size == size, fmt.Sprintf("expected size to be %d (was %d)", size, dc[0].Size))
-						},
+						}),
 					),
 				}
 			},
@@ -166,15 +154,11 @@ func TestVolumeInspect(t *testing.T) {
 					Output: expect.All(
 						expect.Contains(data.Get("vol1")),
 						expect.Contains(data.Get("vol2")),
-						func(stdout string, info string, t *testing.T) {
-							var dc []native.Volume
-							if err := json.Unmarshal([]byte(stdout), &dc); err != nil {
-								t.Fatal(err)
-							}
+						expect.JSON([]native.Volume{}, func(dc []native.Volume, info string, t tig.T) {
 							assert.Assert(t, len(dc) == 2, fmt.Sprintf("two results, not %d", len(dc)))
 							assert.Assert(t, dc[0].Name == data.Get("vol1"), fmt.Sprintf("expected name to be %q (was %q)", data.Get("vol1"), dc[0].Name))
 							assert.Assert(t, dc[1].Name == data.Get("vol2"), fmt.Sprintf("expected name to be %q (was %q)", data.Get("vol2"), dc[1].Name))
-						},
+						}),
 					),
 				}
 			},
@@ -190,14 +174,10 @@ func TestVolumeInspect(t *testing.T) {
 					Errors:   []error{errdefs.ErrNotFound, errdefs.ErrInvalidArgument},
 					Output: expect.All(
 						expect.Contains(data.Get("vol1")),
-						func(stdout string, info string, t *testing.T) {
-							var dc []native.Volume
-							if err := json.Unmarshal([]byte(stdout), &dc); err != nil {
-								t.Fatal(err)
-							}
+						expect.JSON([]native.Volume{}, func(dc []native.Volume, info string, t tig.T) {
 							assert.Assert(t, len(dc) == 1, fmt.Sprintf("one result, not %d", len(dc)))
 							assert.Assert(t, dc[0].Name == data.Get("vol1"), fmt.Sprintf("expected name to be %q (was %q)", data.Get("vol1"), dc[0].Name))
-						},
+						}),
 					),
 				}
 			},
diff --git a/mod/tigron/expect/comparators_test.go b/mod/tigron/expect/comparators_test.go
index c6710f60..f216d3ec 100644
--- a/mod/tigron/expect/comparators_test.go
+++ b/mod/tigron/expect/comparators_test.go
@@ -20,24 +20,45 @@ package expect_test
 // TODO: add a lot more tests including failure conditions with mimicry
 
 import (
+	"encoding/json"
 	"regexp"
 	"testing"
 
 	"github.com/containerd/nerdctl/mod/tigron/expect"
+	"github.com/containerd/nerdctl/mod/tigron/internal/assertive"
+	"github.com/containerd/nerdctl/mod/tigron/tig"
 )
 
 func TestExpect(t *testing.T) {
 	t.Parallel()
 
-	expect.Contains("b")("a b c", "info", t)
-	expect.DoesNotContain("d")("a b c", "info", t)
-	expect.Equals("a b c")("a b c", "info", t)
-	expect.Match(regexp.MustCompile("[a-z ]+"))("a b c", "info", t)
+	expect.Contains("b")("a b c", "contains works", t)
+	expect.DoesNotContain("d")("a b c", "does not contain works", t)
+	expect.Equals("a b c")("a b c", "equals work", t)
+	expect.Match(regexp.MustCompile("[a-z ]+"))("a b c", "match works", t)
 
 	expect.All(
 		expect.Contains("b"),
 		expect.DoesNotContain("d"),
 		expect.Equals("a b c"),
 		expect.Match(regexp.MustCompile("[a-z ]+")),
-	)("a b c", "info", t)
+	)("a b c", "all", t)
+
+	type foo struct {
+		Foo map[string]string `json:"foo"`
+	}
+
+	data, err := json.Marshal(&foo{
+		Foo: map[string]string{
+			"foo": "bar",
+		},
+	})
+
+	assertive.ErrorIsNil(t, err)
+
+	expect.JSON(&foo{}, nil)(string(data), "json, no verifier", t)
+
+	expect.JSON(&foo{}, func(obj *foo, info string, t tig.T) {
+		assertive.IsEqual(t, obj.Foo["foo"], "bar", info)
+	})(string(data), "json, with verifier", t)
 }
diff --git a/mod/tigron/internal/com/command_test.go b/mod/tigron/internal/com/command_test.go
index 7652a772..9c5d0b7e 100644
--- a/mod/tigron/internal/com/command_test.go
+++ b/mod/tigron/internal/com/command_test.go
@@ -411,7 +411,6 @@ func TestStdoutStderr(t *testing.T) {
 func TestTimeoutPlain(t *testing.T) {
 	t.Parallel()
 
-	start := time.Now()
 	command := &com.Command{
 		Binary: "bash",
 		// XXX unclear if windows is really able to terminate sleep 5, so, split it up to give it a
@@ -421,11 +420,10 @@ func TestTimeoutPlain(t *testing.T) {
 	}
 
 	err := command.Run(context.WithValue(context.Background(), com.LoggerKey, t))
-
 	assertive.ErrorIsNil(t, err, "Err")
 
+	start := time.Now()
 	res, err := command.Wait()
-
 	end := time.Now()
 
 	assertive.ErrorIs(t, err, com.ErrTimeout, "Err")
@@ -438,7 +436,6 @@ func TestTimeoutPlain(t *testing.T) {
 func TestTimeoutDelayed(t *testing.T) {
 	t.Parallel()
 
-	start := time.Now()
 	command := &com.Command{
 		Binary: "bash",
 		// XXX unclear if windows is really able to terminate sleep 5, so, split it up to give it a
@@ -448,20 +445,20 @@ func TestTimeoutDelayed(t *testing.T) {
 	}
 
 	err := command.Run(context.WithValue(context.Background(), com.LoggerKey, t))
-
 	assertive.ErrorIsNil(t, err, "Err")
 
-	time.Sleep(1 * time.Second)
+	start := time.Now()
 
-	res, err := command.Wait()
+	time.Sleep(2 * time.Second)
 
+	res, err := command.Wait()
 	end := time.Now()
 
 	assertive.ErrorIs(t, err, com.ErrTimeout, "Err")
 	assertive.IsEqual(t, res.ExitCode, -1, "ExitCode")
 	assertive.IsEqual(t, res.Stdout, "one", "Stdout")
 	assertive.IsEqual(t, res.Stderr, "", "Stderr")
-	assertive.IsLessThan(t, end.Sub(start), 2*time.Second, "Total execution time")
+	assertive.IsLessThan(t, end.Sub(start), 3*time.Second, "Total execution time")
 }
 
 func TestPTYStdout(t *testing.T) {
diff --git a/mod/tigron/test/case.go b/mod/tigron/test/case.go
index 3ec3574a..6a703e96 100644
--- a/mod/tigron/test/case.go
+++ b/mod/tigron/test/case.go
@@ -17,10 +17,13 @@
 package test
 
 import (
+	"encoding/json"
+	"fmt"
 	"slices"
 	"testing"
 
 	"github.com/containerd/nerdctl/mod/tigron/internal/assertive"
+	"github.com/containerd/nerdctl/mod/tigron/internal/formatter"
 )
 
 // Case describes an entire test-case, including data, setup and cleanup routines, command and
@@ -62,9 +65,15 @@ type Case struct {
 	parent  *Case
 }
 
+const (
+	startDecorator  = "🚀"
+	cleanDecorator  = "🧽"
+	setupDecorator  = "🏗"
+	subinDecorator  = "⤵️"
+	suboutDecorator = "↩️"
+)
+
 // Run prepares and executes the test, and any possible subtests.
-//
-//nolint:gocognit
 func (test *Case) Run(t *testing.T) {
 	t.Helper()
 	// Run the test
@@ -72,17 +81,17 @@ func (test *Case) Run(t *testing.T) {
 	testRun := func(subT *testing.T) {
 		subT.Helper()
 
-		assertive.True(subT, test.t == nil, "You cannot run a test multiple times")
+		silentT := assertive.WithSilentSuccess(subT)
+
+		assertive.True(silentT, test.t == nil, "You cannot run a test multiple times")
+		assertive.True(silentT, test.Description != "" || test.parent == nil,
+			"A subtest description cannot be empty")
+		assertive.True(silentT, test.Command == nil || test.Expected != nil,
+			"Expectations for a test command cannot be nil. You may want to use `Setup` instead"+
+				"of `Command`.")
 
 		// Attach testing.T
 		test.t = subT
-		assertive.True(
-			test.t,
-			test.Description != "" || test.parent == nil,
-			"A test description cannot be empty",
-		)
-		assertive.True(test.t, test.Command == nil || test.Expected != nil,
-			"Expectations for a test command cannot be nil. You may want to use Setup instead.")
 
 		// Ensure we have env
 		if test.Env == nil {
@@ -168,49 +177,83 @@ func (test *Case) Run(t *testing.T) {
 		}
 
 		// Execute cleanups now
-		test.t.Log("")
-		test.t.Log("======================== Pre-test cleanup ========================")
-
-		for _, cleanup := range cleanups {
-			cleanup(test.Data, test.helpers)
-		}
-
-		// Register the cleanups, in reverse
-		test.t.Cleanup(func() {
-			test.t.Log("")
-			test.t.Log("======================== Post-test cleanup ========================")
-
-			slices.Reverse(cleanups)
+		if len(cleanups) > 0 {
+			test.t.Log(
+				"\n\n" + formatter.Table(
+					[][]any{{cleanDecorator, fmt.Sprintf("%q: initial cleanup", test.t.Name())}},
+					"=",
+				) + "\n",
+			)
 
 			for _, cleanup := range cleanups {
 				cleanup(test.Data, test.helpers)
 			}
-		})
 
-		// Run the setups
-		test.t.Log("")
-		test.t.Log("======================== Test setup ========================")
+			// Register the cleanups, in reverse
+			test.t.Cleanup(func() {
+				test.t.Helper()
+				test.t.Log(
+					"\n\n" + formatter.Table(
+						[][]any{{cleanDecorator, fmt.Sprintf("%q: post-cleanup", test.t.Name())}},
+						"=",
+					) + "\n",
+				)
+
+				slices.Reverse(cleanups)
 
-		for _, setup := range setups {
-			setup(test.Data, test.helpers)
+				for _, cleanup := range cleanups {
+					cleanup(test.Data, test.helpers)
+				}
+			})
+		}
+
+		// Run the setups
+		if len(setups) > 0 {
+			test.t.Log(
+				"\n\n" + formatter.Table(
+					[][]any{{setupDecorator, fmt.Sprintf("%q: setup", test.t.Name())}},
+					"=",
+				) + "\n",
+			)
+
+			for _, setup := range setups {
+				setup(test.Data, test.helpers)
+			}
 		}
 
 		// Run the command if any, with expectations
 		// Note: if we have a command, we already know we DO have Expected
-		test.t.Log("")
-		test.t.Log("======================== Test Run ========================")
-
 		if test.Command != nil {
-			test.Command(test.Data, test.helpers).Run(test.Expected(test.Data, test.helpers))
+			cmd := test.Command(test.Data, test.helpers)
+
+			debugConfig, _ := json.MarshalIndent(test.Config.(*config).config, "", "  ")
+			debugData, _ := json.MarshalIndent(test.Data.(*data).labels, "", "  ")
+
+			test.t.Log(
+				"\n\n" + formatter.Table(
+					[][]any{
+						{startDecorator, fmt.Sprintf("%q: starting test!", test.t.Name())},
+						{"cwd", test.Data.TempDir()},
+						{"config", string(debugConfig)},
+						{"data", string(debugData)},
+					},
+					"=",
+				) + "\n",
+			)
+
+			cmd.Run(test.Expected(test.Data, test.helpers))
 		}
 
-		// Now go for the subtests
-		test.t.Log("")
-		test.t.Log("======================== Processing subtests ========================")
+		if len(test.SubTests) > 0 {
+			// Now go for the subtests
+			test.t.Logf("\n%s️ %q: into subtests prep", subinDecorator, test.t.Name())
+
+			for _, subTest := range test.SubTests {
+				subTest.parent = test
+				subTest.Run(test.t)
+			}
 
-		for _, subTest := range test.SubTests {
-			subTest.parent = test
-			subTest.Run(test.t)
+			test.t.Logf("\n%s️ %q: done with subtests prep", suboutDecorator, test.t.Name())
 		}
 	}
 
diff --git a/mod/tigron/test/command.go b/mod/tigron/test/command.go
index 67289079..0c1970d8 100644
--- a/mod/tigron/test/command.go
+++ b/mod/tigron/test/command.go
@@ -14,14 +14,14 @@
    limitations under the License.
 */
 
-//nolint:revive
+//revive:disable:exported,package-comments,add-constant // TODO.
 package test
 
 import (
 	"context"
-	"fmt"
 	"io"
 	"os"
+	"strconv"
 	"strings"
 	"testing"
 	"time"
@@ -29,9 +29,17 @@ import (
 	"github.com/containerd/nerdctl/mod/tigron/internal"
 	"github.com/containerd/nerdctl/mod/tigron/internal/assertive"
 	"github.com/containerd/nerdctl/mod/tigron/internal/com"
+	"github.com/containerd/nerdctl/mod/tigron/internal/formatter"
 )
 
-const defaultExecutionTimeout = 3 * time.Minute
+const (
+	defaultExecutionTimeout = 3 * time.Minute
+	commandDecorator        = "⚙️"
+	errorDecorator          = "🚫"
+	exitDecorator           = "⚠️"
+	stdoutDecorator         = "🟢"
+	stderrDecorator         = "🟠"
+)
 
 // CustomizableCommand is an interface meant for people who want to heavily customize the base
 // command of their test case.
@@ -71,7 +79,6 @@ type CustomizableCommand interface {
 	read(key ConfigKey) ConfigValue
 }
 
-//nolint:ireturn
 func NewGenericCommand() CustomizableCommand {
 	genericCom := &GenericCommand{
 		Env: map[string]string{},
@@ -151,49 +158,56 @@ func (gc *GenericCommand) Signal(sig os.Signal) error {
 }
 
 func (gc *GenericCommand) Run(expect *Expected) {
-	if gc.t != nil {
-		gc.t.Helper()
-	}
+	gc.t.Helper()
+
+	var debug [][]any
 
 	if !gc.async {
 		_ = gc.cmd.Run(context.WithValue(context.Background(), com.LoggerKey, gc.t))
 	}
 
+	debug = append(debug,
+		[]any{"➡️", commandDecorator + " " + gc.cmd.Binary + " " + strings.Join(gc.cmd.Args, " ")},
+	)
+
+	// Wait for the command and if Err is not nil, append it to the debug information
 	result, err := gc.cmd.Wait()
+	if err != nil {
+		debug = append(debug, []any{"", errorDecorator + " " + err.Error()})
+	}
+
+	// If we were requested to perform expectation matching, add non-empty debugging information
 	if result != nil {
 		gc.rawStdErr = result.Stderr
+
+		if result.ExitCode != 0 {
+			debug = append(debug, []any{"", exitDecorator + " " + strconv.Itoa(result.ExitCode)})
+		}
+
+		if result.Stdout != "" {
+			debug = append(debug, []any{"", stdoutDecorator + " " + result.Stdout})
+		}
+
+		if result.Stderr != "" {
+			debug = append(debug, []any{"", stderrDecorator + " " + result.Stderr})
+		}
+
+		if result.Signal != nil {
+			debug = append(debug, []any{"Signal", result.Signal.String()})
+		}
+
+		debug = append(debug,
+			[]any{"Limit", gc.cmd.Timeout},
+			[]any{"Environ", strings.Join(result.Environ, "\n")},
+		)
 	}
 
-	// Check our expectations, if any
+	// Print the command info
+	gc.t.Log("\n\n" + formatter.Table(debug, "-") + "\n")
+
+	// Now, check our expectations, if any
 	if expect != nil {
-		// Build the debug string
-		separator := "================================="
-		debugCommand := gc.cmd.Binary + " " + strings.Join(gc.cmd.Args, " ")
-		debugTimeout := gc.cmd.Timeout
-		debugWD := gc.cmd.WorkingDir
-
-		// FIXME: this is ugly af. Do better.
-		debug := fmt.Sprintf(
-			"\n%s\n| Command:\t%s\n| Working Dir:\t%s\n| Timeout:\t%s\n%s\n"+
-				"%s\n%s\n| Stderr:\n%s\n%s\n%s\n| Stdout:\n%s\n%s\n%s\n| Exit Code: %d\n| Signaled: %v\n| Err: %v\n%s",
-			separator,
-			debugCommand,
-			debugWD,
-			debugTimeout,
-			separator,
-			"\t"+strings.Join(result.Environ, "\n\t"),
-			separator,
-			separator,
-			result.Stderr,
-			separator,
-			separator,
-			result.Stdout,
-			separator,
-			result.ExitCode,
-			result.Signal,
-			err,
-			separator,
-		)
+		assertT := assertive.WithSilentSuccess(gc.t)
 
 		// ExitCode goes first
 		switch expect.ExitCode {
@@ -203,44 +217,67 @@ func (gc *GenericCommand) Run(expect *Expected) {
 			// ExitCodeGenericFail means we expect an error (excluding timeout, cancellation,
 			// signalling).
 			assertive.ErrorIs(
-				gc.t,
+				assertT,
 				err,
 				com.ErrExecutionFailed,
-				"Command should have failed",
-				debug,
+				"Command should fail",
 			)
 		case internal.ExitCodeTimeout:
 			assertive.ErrorIs(
 				gc.t,
 				err,
 				com.ErrTimeout,
-				"Command should have timed out",
-				debug,
+				"Command should time-out",
 			)
 		case internal.ExitCodeSignaled:
 			assertive.ErrorIs(
 				gc.t,
 				err,
 				com.ErrSignaled,
-				"Command should have been signaled",
-				debug,
+				"Command should get signaled",
 			)
 		case internal.ExitCodeSuccess:
-			assertive.ErrorIsNil(gc.t, err, "Command should have succeeded", debug)
+			assertive.ErrorIsNil(
+				assertT,
+				err,
+				"Command should succeed",
+			)
 		default:
-			assertive.IsEqual(gc.t, expect.ExitCode, result.ExitCode,
-				fmt.Sprintf("Expected exit code: %d\n", expect.ExitCode), debug)
+			exc := -1
+			if result != nil {
+				exc = result.ExitCode
+			}
+
+			assertive.IsEqual(
+				assertT,
+				expect.ExitCode,
+				exc,
+				"Exit code should match expectation",
+			)
 		}
 
+		// Switch to fail later mode so that we get ALL subsequent asserts failures from there on.
+		// Also does allow using assert(ive) in go routines.
+		assertT = assertive.WithFailLater(gc.t)
+
 		// Range through the expected errors and confirm they are seen on stderr
 		for _, expectErr := range expect.Errors {
-			assertive.Contains(gc.t, result.Stderr, expectErr.Error(),
-				fmt.Sprintf("Expected error: %q to be found in stderr\n", expectErr.Error()), debug)
+			assertive.Contains(
+				assertT,
+				gc.rawStdErr,
+				expectErr.Error(),
+				"Stderr should contain expected error",
+			)
 		}
 
 		// Finally, check the output if we are asked to
+		// FIXME: we cannot pass on assertT further for now until we move to tig.T
 		if expect.Output != nil {
-			expect.Output(result.Stdout, debug, gc.t)
+			expect.Output(
+				result.Stdout,
+				"",
+				gc.t,
+			)
 		}
 	}
 }
@@ -263,7 +300,6 @@ func (gc *GenericCommand) withConfig(config Config) {
 	gc.Config = config
 }
 
-//nolint:ireturn
 func (gc *GenericCommand) Clone() TestableCommand {
 	// Copy the command and return a new one - with almost everything from the parent command
 	clone := *gc
@@ -287,7 +323,6 @@ func (gc *GenericCommand) T() *testing.T {
 	return gc.t
 }
 
-//nolint:ireturn
 func (gc *GenericCommand) clear() TestableCommand {
 	comcopy := *gc
 	// Reset internal command
diff --git a/mod/tigron/test/helpers.go b/mod/tigron/test/helpers.go
index 68122604..c148be6e 100644
--- a/mod/tigron/test/helpers.go
+++ b/mod/tigron/test/helpers.go
@@ -32,6 +32,7 @@ type helpersInternal struct {
 
 // Ensure will run a command and make sure it is successful.
 func (help *helpersInternal) Ensure(args ...string) {
+	help.t.Helper()
 	help.Command(args...).Run(&Expected{
 		ExitCode: internal.ExitCodeSuccess,
 	})
@@ -39,6 +40,7 @@ func (help *helpersInternal) Ensure(args ...string) {
 
 // Anyhow will run a command regardless of outcome (may or may not fail).
 func (help *helpersInternal) Anyhow(args ...string) {
+	help.t.Helper()
 	help.Command(args...).Run(&Expected{
 		ExitCode: internal.ExitCodeNoCheck,
 	})
@@ -46,6 +48,7 @@ func (help *helpersInternal) Anyhow(args ...string) {
 
 // Fail will run a command and make sure it does fail.
 func (help *helpersInternal) Fail(args ...string) {
+	help.t.Helper()
 	help.Command(args...).Run(&Expected{
 		ExitCode: internal.ExitCodeGenericFail,
 	})
@@ -55,6 +58,7 @@ func (help *helpersInternal) Fail(args ...string) {
 func (help *helpersInternal) Capture(args ...string) string {
 	var ret string
 
+	help.t.Helper()
 	help.Command(args...).Run(&Expected{
 		//nolint:thelper
 		Output: func(stdout, _ string, _ *testing.T) {
@@ -67,6 +71,7 @@ func (help *helpersInternal) Capture(args ...string) string {
 
 // Err will run a command with no expectation and return Stderr.
 func (help *helpersInternal) Err(args ...string) string {
+	help.t.Helper()
 	cmd := help.Command(args...)
 	cmd.Run(nil)
 
diff --git a/pkg/testutil/nerdtest/command.go b/pkg/testutil/nerdtest/command.go
index b979963d..dbeed949 100644
--- a/pkg/testutil/nerdtest/command.go
+++ b/pkg/testutil/nerdtest/command.go
@@ -105,6 +105,7 @@ type nerdCommand struct {
 }
 
 func (nc *nerdCommand) Run(expect *test.Expected) {
+	nc.T().Helper()
 	nc.prep()
 	if getTarget() == targetDocker {
 		// We are not in the business of testing docker *error* output, so, spay expectation here
diff --git a/pkg/testutil/nerdtest/utilities.go b/pkg/testutil/nerdtest/utilities.go
index 59311e5f..56cae946 100644
--- a/pkg/testutil/nerdtest/utilities.go
+++ b/pkg/testutil/nerdtest/utilities.go
@@ -45,6 +45,7 @@ func IsDocker() bool {
 
 // InspectContainer is a helper that can be used inside custom commands or Setup
 func InspectContainer(helpers test.Helpers, name string) dockercompat.Container {
+	helpers.T().Helper()
 	var dc []dockercompat.Container
 	cmd := helpers.Command("container", "inspect", name)
 	cmd.Run(&test.Expected{
@@ -58,6 +59,7 @@ func InspectContainer(helpers test.Helpers, name string) dockercompat.Container
 }
 
 func InspectVolume(helpers test.Helpers, name string) native.Volume {
+	helpers.T().Helper()
 	var dc []native.Volume
 	cmd := helpers.Command("volume", "inspect", name)
 	cmd.Run(&test.Expected{
@@ -71,6 +73,7 @@ func InspectVolume(helpers test.Helpers, name string) native.Volume {
 }
 
 func InspectNetwork(helpers test.Helpers, name string) dockercompat.Network {
+	helpers.T().Helper()
 	var dc []dockercompat.Network
 	cmd := helpers.Command("network", "inspect", name)
 	cmd.Run(&test.Expected{
@@ -84,6 +87,7 @@ func InspectNetwork(helpers test.Helpers, name string) dockercompat.Network {
 }
 
 func InspectImage(helpers test.Helpers, name string) dockercompat.Image {
+	helpers.T().Helper()
 	var dc []dockercompat.Image
 	cmd := helpers.Command("image", "inspect", name)
 	cmd.Run(&test.Expected{
@@ -102,6 +106,7 @@ const (
 )
 
 func EnsureContainerStarted(helpers test.Helpers, con string) {
+	helpers.T().Helper()
 	started := false
 	for i := 0; i < maxRetry && !started; i++ {
 		helpers.Command("container", "inspect", con).