This commit is contained in:
Xienan Fang 2026-07-24 22:44:35 +00:00 committed by GitHub
commit 9152cda87c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 2446 additions and 1218 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
package presentation
import (
"encoding/json"
"fmt"
"math"
"reflect"
@ -17,6 +18,22 @@ import (
"github.com/samber/lo"
)
var PATHS_TO_CONVERT_BIGMETRICS = []map[string]string{
{"ClientStats.cpu_stats.cpu_usage.total_usage": "nanoseconds"},
{"ClientStats.cpu_stats.cpu_usage.percpu_usage": "nanoseconds"},
{"ClientStats.cpu_stats.cpu_usage.usage_in_kernelmode": "nanoseconds"},
{"ClientStats.cpu_stats.cpu_usage.usage_in_usermode": "nanoseconds"},
{"ClientStats.cpu_stats.system_cpu_usage": "nanoseconds"},
{"ClientStats.precpu_stats.cpu_usage.total_usage": "nanoseconds"},
{"ClientStats.precpu_stats.cpu_usage.percpu_usage": "nanoseconds"},
{"ClientStats.precpu_stats.cpu_usage.usage_in_kernelmode": "nanoseconds"},
{"ClientStats.precpu_stats.cpu_usage.usage_in_usermode": "nanoseconds"},
{"ClientStats.precpu_stats.system_cpu_usage": "nanoseconds"},
{"ClientStats.memory_stats.limit": "bytes"},
{"ClientStats.memory_stats.stats.hierarchical_memory_limit": "bytes"},
{"ClientStats.memory_stats.stats.hierarchical_memsw_limit": "bytes"},
}
func RenderStats(userConfig *config.UserConfig, container *commands.Container, viewWidth int) (string, error) {
stats, ok := container.GetLastStats()
if !ok {
@ -37,7 +54,23 @@ func RenderStats(userConfig *config.UserConfig, container *commands.Container, v
dataReceived := fmt.Sprintf("Traffic received: %s", utils.FormatDecimalBytes(stats.ClientStats.Networks.Eth0.RxBytes))
dataSent := fmt.Sprintf("Traffic sent: %s", utils.FormatDecimalBytes(stats.ClientStats.Networks.Eth0.TxBytes))
originalStats, err := utils.MarshalIntoYaml(stats)
statsJsonBytes, err := json.Marshal(stats)
if err != nil {
return "", err
}
var statsMap map[string]interface{}
err = json.Unmarshal(statsJsonBytes, &statsMap)
if err != nil {
return "", err
}
err = convertBigMetric(&statsMap)
if err != nil {
return "", err
}
originalStats, err := utils.MarshalIntoYaml(statsMap)
if err != nil {
return "", err
}
@ -51,6 +84,7 @@ func RenderStats(userConfig *config.UserConfig, container *commands.Container, v
)
return contents, nil
}
// plotGraph returns the plotted graph based on the graph spec and the stat history
@ -144,3 +178,78 @@ func getFloat(unk interface{}) (float64, error) {
}
}
}
func convertBigMetric(data *map[string]interface{}) error {
// switch value := schema.(type) {
// case map[string]interface{}:
// for key, val := range value {
// path = fmt.Sprintf("%s.%s", path, key)
// if err := convertBigMetricFromSchema(data, path, val); err != nil {
// return err
// }
// }
// case []string:
// // use path to translate from []int to []string
// fmt.Println("Converting []int64 to []string for path:", path)
// metric, err := lookup.LookupString(data, strings.TrimPrefix(path, "."))
// if err != nil {
// return err
// }
// if reflect.TypeOf(metric.Interface()) != reflect.TypeOf([]int64{}) {
// return fmt.Errorf("Can't convert non []int64 %v to []string", reflect.TypeOf(metric.Interface()))
// } else {
// longIntSlice := metric.Interface().([]int64)
// formattedMetric := lo.Map(longIntSlice, func(val int64, index int) string {
// return utils.FormatBigMetric(val, value[index])
// })
// return utils.SetObjectFieldByPath(data, path, formattedMetric)
// }
// case string:
// // use path to translate from int/int64 to string
// fmt.Println("Converting int64 to string for path:", path)
// metric, err := lookup.LookupString(data, strings.TrimPrefix(path, "."))
// if err != nil {
// return err
// }
// if reflect.TypeOf(metric.Interface()) != reflect.TypeOf(int64(0)) {
// return fmt.Errorf("Can't convert non int64 %v to string", reflect.TypeOf(metric.Interface()))
// } else {
// formattedMetric := utils.FormatBigMetric(metric.Interface().(int64), value)
// return utils.SetObjectFieldByPath(data, path, formattedMetric)
// }
// }
// return nil
for _, pathToConvert := range PATHS_TO_CONVERT_BIGMETRICS {
for path, metricType := range pathToConvert {
metric, err := lookup.LookupString(data, path)
if err != nil {
if err == lookup.ErrKeyNotFound {
continue
}
return err
}
if !metric.IsValid() {
continue
}
if reflect.TypeOf(metric.Interface()) == reflect.TypeOf(float64(0)) {
formattedMetric := utils.FormatBigMetric(int64(metric.Interface().(float64)), metricType)
err = utils.SetObjectFieldByPath(data, fmt.Sprintf(".%s", path), formattedMetric)
if err != nil {
return err
}
} else if reflect.TypeOf(metric.Interface()) == reflect.TypeOf([]float64{}) {
longIntSlice := lo.Map(metric.Interface().([]float64), func(val float64, index int) int64 {
return int64(val)
})
formattedMetric := lo.Map(longIntSlice, func(val int64, index int) string {
return utils.FormatBigMetric(val, metricType)
})
err = utils.SetObjectFieldByPath(data, fmt.Sprintf(".%s", path), formattedMetric)
if err != nil {
return err
}
}
}
}
return nil
}

View file

@ -0,0 +1,224 @@
package presentation
import (
"encoding/json"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestConvertBigMetricFromSchema(t *testing.T) {
type scenario struct {
name string
data map[string]interface{}
expected map[string]interface{}
expectedErr string
}
inJSONBytes, err := os.ReadFile("testdata/stats.json")
if err != nil {
t.Fatalf("failed to read testdata/stats.json: %v", err)
}
loadInJSON := func() map[string]interface{} {
var m map[string]interface{}
if err := json.Unmarshal(inJSONBytes, &m); err != nil {
t.Fatalf("failed to unmarshal testdata/stats.json: %v", err)
}
return m
}
setNested := func(m map[string]interface{}, keys []string, val interface{}) {
for _, k := range keys[:len(keys)-1] {
m = m[k].(map[string]interface{})
}
m[keys[len(keys)-1]] = val
}
inJSONExpected := loadInJSON()
setNested(inJSONExpected, []string{"ClientStats", "memory_stats", "limit"}, "1.911GB")
setNested(inJSONExpected, []string{"ClientStats", "memory_stats", "stats", "hierarchical_memory_limit"}, "0")
setNested(inJSONExpected, []string{"ClientStats", "memory_stats", "stats", "hierarchical_memsw_limit"}, "0")
setNested(inJSONExpected, []string{"ClientStats", "cpu_stats", "cpu_usage", "total_usage"}, "30.283ms")
setNested(inJSONExpected, []string{"ClientStats", "cpu_stats", "cpu_usage", "usage_in_kernelmode"}, "15.664ms")
setNested(inJSONExpected, []string{"ClientStats", "cpu_stats", "cpu_usage", "usage_in_usermode"}, "14.619ms")
setNested(inJSONExpected, []string{"ClientStats", "cpu_stats", "system_cpu_usage"}, "25.994h")
setNested(inJSONExpected, []string{"ClientStats", "precpu_stats", "cpu_usage", "total_usage"}, "30.283ms")
setNested(inJSONExpected, []string{"ClientStats", "precpu_stats", "cpu_usage", "usage_in_kernelmode"}, "15.664ms")
setNested(inJSONExpected, []string{"ClientStats", "precpu_stats", "cpu_usage", "usage_in_usermode"}, "14.619ms")
setNested(inJSONExpected, []string{"ClientStats", "precpu_stats", "system_cpu_usage"}, "25.994h")
scenarios := []scenario{
{
name: "string schema: converts float64 bytes",
data: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"limit": float64(2048),
},
},
},
expected: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"limit": "2.000KB",
},
},
},
},
{
name: "string schema: converts int64 nanoseconds",
data: map[string]interface{}{
"ClientStats": map[string]interface{}{
"cpu_stats": map[string]interface{}{
"cpu_usage": map[string]interface{}{
"total_usage": float64(100000000000),
"percpu_usage": []float64{50000000000, 50000000000},
"usage_in_kernelmode": float64(200000),
"usage_in_usermode": float64(200000),
},
"system_cpu_usage": float64(100000000000),
},
},
},
expected: map[string]interface{}{
"ClientStats": map[string]interface{}{
"cpu_stats": map[string]interface{}{
"cpu_usage": map[string]interface{}{
"total_usage": "100.000s",
"percpu_usage": []string{"50.000s", "50.000s"},
"usage_in_kernelmode": "200.000µs",
"usage_in_usermode": "200.000µs",
},
"system_cpu_usage": "100.000s",
},
},
},
},
{
name: "empty data is a no-op",
data: map[string]interface{}{},
expected: map[string]interface{}{},
},
{
name: "bytes below unit threshold stays as raw number",
data: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"limit": float64(500),
},
},
},
expected: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"limit": "500",
},
},
},
},
{
name: "bytes at MB scale",
data: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"limit": float64(1500000),
},
},
},
expected: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"limit": "1.431MB",
},
},
},
},
{
name: "converts deeply nested hierarchical memory limits",
data: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"stats": map[string]interface{}{
"hierarchical_memory_limit": float64(1073741824),
"hierarchical_memsw_limit": float64(2147483648),
},
},
},
},
expected: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"stats": map[string]interface{}{
"hierarchical_memory_limit": "1.000GB",
"hierarchical_memsw_limit": "2.000GB",
},
},
},
},
},
{
name: "converts precpu_stats branch",
data: map[string]interface{}{
"ClientStats": map[string]interface{}{
"precpu_stats": map[string]interface{}{
"cpu_usage": map[string]interface{}{
"total_usage": float64(1500000),
"percpu_usage": []float64{1000, 2000000000},
},
"system_cpu_usage": float64(1500000000000),
},
},
},
expected: map[string]interface{}{
"ClientStats": map[string]interface{}{
"precpu_stats": map[string]interface{}{
"cpu_usage": map[string]interface{}{
"total_usage": "1.500ms",
"percpu_usage": []string{"1.000µs", "2.000s"},
},
"system_cpu_usage": "1.500m",
},
},
},
},
{
name: "preserves fields not listed in PATHS_TO_CONVERT_BIGMETRICS",
data: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"limit": float64(2048),
"usage": float64(1024),
},
"name": "my-container",
},
},
expected: map[string]interface{}{
"ClientStats": map[string]interface{}{
"memory_stats": map[string]interface{}{
"limit": "2.000KB",
"usage": float64(1024),
},
"name": "my-container",
},
},
},
{
name: "converts realistic docker stats loaded from testdata/stats.json",
data: loadInJSON(),
expected: inJSONExpected,
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
data := s.data
err := convertBigMetric(&data)
if s.expectedErr != "" {
assert.EqualError(t, err, s.expectedErr)
} else {
assert.NoError(t, err)
}
assert.Equal(t, s.expected, data)
})
}
}

122
pkg/gui/presentation/testdata/stats.json vendored Normal file
View file

@ -0,0 +1,122 @@
{
"ClientStats": {
"blkio_stats": {
"io_merged_recursive": null,
"io_queue_recursive": null,
"io_service_bytes_recursive": [
{
"major": 253,
"minor": 0,
"op": "read",
"value": 0
},
{
"major": 253,
"minor": 0,
"op": "write",
"value": 12288
}
],
"io_service_time_recursive": null,
"io_serviced_recursive": null,
"io_time_recursive": null,
"io_wait_time_recursive": null,
"sectors_recursive": null
},
"cpu_stats": {
"cpu_usage": {
"percpu_usage": null,
"total_usage": 30283000,
"usage_in_kernelmode": 15664000,
"usage_in_usermode": 14619000
},
"online_cpus": 2,
"system_cpu_usage": 25994230660000000,
"throttling_data": {
"periods": 0,
"throttled_periods": 0,
"throttled_time": 0
}
},
"id": "c7dc2bd078294bd06166be7c1cf680cb3ba968a04669b014b2b99666e08c8d57",
"memory_stats": {
"limit": 2051391488,
"max_usage": 0,
"stats": {
"active_anon": 2695168,
"active_file": 0,
"cache": 0,
"dirty": 0,
"hierarchical_memory_limit": 0,
"hierarchical_memsw_limit": 0,
"inactive_anon": 0,
"inactive_file": 69632,
"mapped_file": 0,
"pgfault": 4656,
"pgmajfault": 0,
"pgpgin": 0,
"pgpgout": 0,
"rss": 0,
"rss_huge": 0,
"total_active_anon": 0,
"total_active_file": 0,
"total_cache": 0,
"total_dirty": 0,
"total_inactive_anon": 0,
"total_inactive_file": 0,
"total_mapped_file": 0,
"total_pgfault": 0,
"total_pgmajfault": 0,
"total_pgpgin": 0,
"total_pgpgout": 0,
"total_rss": 0,
"total_rss_huge": 0,
"total_unevictable": 0,
"total_writeback": 0,
"unevictable": 0,
"writeback": 0
},
"usage": 3465216
},
"name": "/hungry_margulis",
"networks": {
"eth0": {
"rx_bytes": 112796,
"rx_dropped": 0,
"rx_errors": 0,
"rx_packets": 1610,
"tx_bytes": 0,
"tx_dropped": 0,
"tx_errors": 0,
"tx_packets": 0
}
},
"num_procs": 0,
"pids_stats": {
"current": 3
},
"precpu_stats": {
"cpu_usage": {
"percpu_usage": null,
"total_usage": 30283000,
"usage_in_kernelmode": 15664000,
"usage_in_usermode": 14619000
},
"online_cpus": 2,
"system_cpu_usage": 25994228620000000,
"throttling_data": {
"periods": 0,
"throttled_periods": 0,
"throttled_time": 0
}
},
"preread": "2026-07-24T21:52:31.132423289Z",
"read": "2026-07-24T21:52:32.151677303Z",
"storage_stats": {}
},
"DerivedStats": {
"CPUPercentage": 0,
"MemoryPercentage": 0.16892026803613217
},
"RecordedAt": "2026-07-24T22:52:32.092566+01:00"
}

View file

@ -410,3 +410,76 @@ func marshalIntoFormat(data interface{}, format string) ([]byte, error) {
return nil, errors.New(fmt.Sprintf("Unsupported detailization format: %s", format))
}
}
// FormatBigMetric takes a big metric and formats it into a human readable string with the appropriate unit.
// For example, if you give it 1000000 with a base unit of bytes, it will return "1.000 MB".
// If you give it 1000000000 with a base unit of nanoseconds, it will return "1.000 s"
func FormatBigMetric(number int64, baseUnitName string) string {
memoryUnits := []string{"KB", "MB", "GB", "TB"}
cpuUnits := []string{"µs", "ms", "s", "m", "h"}
var unit int64
if baseUnitName == "bytes" {
unit = 1024
} else {
unit = 1000
}
if number < unit {
return fmt.Sprintf("%d", number)
}
div, exp := int64(unit), 1
for n := number / unit; n >= unit; n /= unit {
div *= unit
exp++
}
switch baseUnitName {
case "bytes":
return fmt.Sprintf("%.3f%s", float64(number)/float64(div), memoryUnits[exp-1])
case "nanoseconds":
return fmt.Sprintf("%.3f%s", float64(number)/float64(div), cpuUnits[exp-1])
default:
return fmt.Sprintf("%d", number)
}
}
// SetObjectFieldByPath takes an object, a path to a field in that object, and a value,
// and sets the field at that path to the value.
// For example, if you have an object {"a": {"b": {"c": 1}}}, a path of "a.b.c", and a value of 2,
// it will set the object to {"a": {"b": {"c": 2}}}
func SetObjectFieldByPath(object *map[string]interface{}, path string, value interface{}) error {
re := regexp.MustCompile(`^\.`)
if !re.MatchString(path) && path != "" {
return fmt.Errorf("Invalid path format %s, path should start with a dot", path)
}
targetObject := map[string]interface{}{}
currentObject := *object
targetObject = copyMapByPath(currentObject, targetObject, path, value)
*object = targetObject
return nil
}
// copyMapByPath is a helper function for SetObjectFieldByPath
// that recursively copies an object while setting the value at the right path
func copyMapByPath(currentObject map[string]interface{}, targetObject map[string]interface{}, path string, value interface{}) map[string]interface{} {
if path == "" {
return currentObject
}
keyPathSlice := strings.Split(path, ".")[1:]
currentKeys := make([]string, 0, len(currentObject))
for k := range currentObject {
currentKeys = append(currentKeys, k)
}
for _, key := range currentKeys {
if key != keyPathSlice[0] {
targetObject[key] = currentObject[key]
} else {
if len(keyPathSlice) == 1 {
targetObject[key] = value
} else {
nextObject := currentObject[key].(map[string]interface{})
targetObject[key] = copyMapByPath(nextObject, map[string]interface{}{}, "."+strings.Join(keyPathSlice[1:], "."), value)
}
}
}
return targetObject
}

View file

@ -319,3 +319,268 @@ quux:
}
}
}
func TestSetObjectFieldByPath(t *testing.T) {
type scenario struct {
object *map[string]interface{}
path string
value interface{}
expected map[string]interface{}
expectedErr error
}
scenarios := []scenario{
{
object: &map[string]interface{}{
"foo": "replacement",
},
path: ".foo",
value: "bar",
expected: map[string]interface{}{"foo": "bar"},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"bar": "replacement",
},
},
path: ".foo.bar",
value: "bar_1",
expected: map[string]interface{}{"foo": map[string]interface{}{"bar": "bar_1"}},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"foo_1": "foo_1_1",
"foo_2": map[string]interface{}{
"foo_2_1": "foo_2_1_1",
},
"foo_3": map[string]interface{}{
"foo_3_1": "foo_3_1_1",
"foo_3_2": "foo_3_2_1",
"foo_3_3": map[string]interface{}{
"foo_3_3_1": "foo_3_3_1_1",
"foo_3_3_2": "foo_3_3_2_1",
},
},
"bar": map[string]interface{}{
"bar_1": "replacement",
},
},
},
path: ".foo.bar.bar_1",
value: "bar_1_1",
expected: map[string]interface{}{
"foo": map[string]interface{}{
"foo_1": "foo_1_1",
"foo_2": map[string]interface{}{
"foo_2_1": "foo_2_1_1",
},
"foo_3": map[string]interface{}{
"foo_3_1": "foo_3_1_1",
"foo_3_2": "foo_3_2_1",
"foo_3_3": map[string]interface{}{
"foo_3_3_1": "foo_3_3_1_1",
"foo_3_3_2": "foo_3_3_2_1",
},
},
"bar": map[string]interface{}{
"bar_1": "bar_1_1",
},
},
},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"foo_1": "foo_1_1",
"foo_2": map[string]interface{}{
"foo_2_1": "foo_2_1_1",
},
"foo_3": map[string]interface{}{
"foo_3_1": "foo_3_1_1",
"foo_3_2": "foo_3_2_1",
"foo_3_3": map[string]interface{}{
"foo_3_3_1": "foo_3_3_1_1",
"foo_3_3_2": "foo_3_3_2_1",
},
},
"bar": map[string]interface{}{
"bar_1": map[string]interface{}{
"bar_1_1": map[string]interface{}{
"bar_1_1_1": map[string]interface{}{
"bar_1_1_1_1": "replacement",
},
},
},
},
},
},
path: ".foo.bar.bar_1.bar_1_1.bar_1_1_1.bar_1_1_1_1",
value: "bar_1_1_1_1_1",
expected: map[string]interface{}{
"foo": map[string]interface{}{
"foo_1": "foo_1_1",
"foo_2": map[string]interface{}{
"foo_2_1": "foo_2_1_1",
},
"foo_3": map[string]interface{}{
"foo_3_1": "foo_3_1_1",
"foo_3_2": "foo_3_2_1",
"foo_3_3": map[string]interface{}{
"foo_3_3_1": "foo_3_3_1_1",
"foo_3_3_2": "foo_3_3_2_1",
},
},
"bar": map[string]interface{}{
"bar_1": map[string]interface{}{
"bar_1_1": map[string]interface{}{
"bar_1_1_1": map[string]interface{}{
"bar_1_1_1_1": "bar_1_1_1_1_1",
},
},
},
},
},
},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"bar": "replacement",
},
},
path: "",
value: "bar_1",
expected: map[string]interface{}{"foo": map[string]interface{}{"bar": "replacement"}},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"bar": "replacement",
},
},
path: "foo.bar",
value: "bar_1",
expected: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "replacement",
},
},
expectedErr: errors.New("Invalid path format foo.bar, path should start with a dot"),
},
}
for _, s := range scenarios {
err := SetObjectFieldByPath(s.object, s.path, s.value)
assert.EqualValues(t, s.expected, *s.object)
if s.expectedErr != nil {
assert.EqualError(t, err, s.expectedErr.Error())
} else {
assert.NoError(t, err)
}
}
}
func TestFormatBigMetric(t *testing.T) {
tests := []struct {
name string
number int64
baseUnitName string
expected string
}{
// Bytes tests
{
name: "bytes: small value",
number: 500,
baseUnitName: "bytes",
expected: "500",
},
{
name: "bytes: KB",
number: 1500,
baseUnitName: "bytes",
expected: "1.465KB",
},
{
name: "bytes: MB",
number: 1500000,
baseUnitName: "bytes",
expected: "1.431MB",
},
{
name: "bytes: GB",
number: 1500000000,
baseUnitName: "bytes",
expected: "1.397GB",
},
{
name: "bytes: TB",
number: 1500000000000,
baseUnitName: "bytes",
expected: "1.364TB",
},
// Nanoseconds tests
{
name: "nanoseconds: small value",
number: 500,
baseUnitName: "nanoseconds",
expected: "500",
},
{
name: "nanoseconds: µs",
number: 1500,
baseUnitName: "nanoseconds",
expected: "1.500µs",
},
{
name: "nanoseconds: ms",
number: 1500000,
baseUnitName: "nanoseconds",
expected: "1.500ms",
},
{
name: "nanoseconds: s",
number: 1500000000,
baseUnitName: "nanoseconds",
expected: "1.500s",
},
{
name: "nanoseconds: m",
number: 1500000000000,
baseUnitName: "nanoseconds",
expected: "1.500m",
},
{
name: "nanoseconds: h",
number: 1500000000000000,
baseUnitName: "nanoseconds",
expected: "1.500h",
},
// Exact boundaries
{
name: "bytes: exact 1024",
number: 1024,
baseUnitName: "bytes",
expected: "1.000KB",
},
{
name: "bytes: 1023",
number: 1023,
baseUnitName: "bytes",
expected: "1023",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := FormatBigMetric(tt.number, tt.baseUnitName)
assert.EqualValues(t, tt.expected, result)
})
}
}