github.com/go-chrono/chrono@v0.0.0-20240102183611-532f0d0d7c34/duration_test.go (about)

     1  package chrono_test
     2  
     3  import (
     4  	"reflect"
     5  	"runtime"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/go-chrono/chrono"
    10  )
    11  
    12  func TestDurationOf(t *testing.T) {
    13  	for _, tt := range []struct {
    14  		name string
    15  		of   chrono.Extent
    16  		nsec float64
    17  	}{
    18  		{
    19  			name: "of positive nanoseconds",
    20  			of:   90000000 * chrono.Nanosecond,
    21  			nsec: 90000000,
    22  		},
    23  		{
    24  			name: "of positive microsecond",
    25  			of:   90000 * chrono.Microsecond,
    26  			nsec: 90000000,
    27  		},
    28  		{
    29  			name: "of positive milliseconds",
    30  			of:   90 * chrono.Millisecond,
    31  			nsec: 90000000,
    32  		},
    33  		{
    34  			name: "of positive seconds",
    35  			of:   9 * chrono.Second,
    36  			nsec: 9000000000,
    37  		},
    38  		{
    39  			name: "of positive minutes",
    40  			of:   150 * chrono.Minute,
    41  			nsec: 9000000000000,
    42  		},
    43  		{
    44  			name: "of positive hours",
    45  			of:   2 * chrono.Hour,
    46  			nsec: 7200000000000,
    47  		},
    48  		{
    49  			name: "of negative nanoseconds",
    50  			of:   -90000000 * chrono.Nanosecond,
    51  			nsec: -90000000,
    52  		},
    53  		{
    54  			name: "of negative microsecond",
    55  			of:   -90000 * chrono.Microsecond,
    56  			nsec: -90000000,
    57  		},
    58  		{
    59  			name: "of negative milliseconds",
    60  			of:   -90 * chrono.Millisecond,
    61  			nsec: -90000000,
    62  		},
    63  		{
    64  			name: "of negative seconds",
    65  			of:   -9 * chrono.Second,
    66  			nsec: -9000000000,
    67  		},
    68  		{
    69  			name: "of negative minutes",
    70  			of:   -150 * chrono.Minute,
    71  			nsec: -9000000000000,
    72  		},
    73  		{
    74  			name: "of negative hours",
    75  			of:   -2 * chrono.Hour,
    76  			nsec: -7200000000000,
    77  		},
    78  	} {
    79  		t.Run(tt.name, func(t *testing.T) {
    80  			d := chrono.DurationOf(tt.of)
    81  			if out := d.Nanoseconds(); out != tt.nsec {
    82  				t.Errorf("d.Nanoseconds() = %f, want %f", out, tt.nsec)
    83  			}
    84  		})
    85  	}
    86  }
    87  
    88  func TestDuration_units(t *testing.T) {
    89  	for _, tt := range []struct {
    90  		name     string
    91  		of       chrono.Extent
    92  		f        func(chrono.Duration) float64
    93  		expected float64
    94  	}{
    95  		{
    96  			name:     "positive nanoseconds",
    97  			of:       9000000000000 * chrono.Nanosecond,
    98  			f:        chrono.Duration.Nanoseconds,
    99  			expected: 9000000000000,
   100  		},
   101  		{
   102  			name:     "positive microseconds",
   103  			of:       9500 * chrono.Nanosecond,
   104  			f:        chrono.Duration.Microseconds,
   105  			expected: 9.5,
   106  		},
   107  		{
   108  			name:     "positive milliseconds",
   109  			of:       9500 * chrono.Microsecond,
   110  			f:        chrono.Duration.Milliseconds,
   111  			expected: 9.5,
   112  		},
   113  		{
   114  			name:     "positive seconds",
   115  			of:       9500 * chrono.Millisecond,
   116  			f:        chrono.Duration.Seconds,
   117  			expected: 9.5,
   118  		},
   119  		{
   120  			name:     "positive minutes",
   121  			of:       90 * chrono.Second,
   122  			f:        chrono.Duration.Minutes,
   123  			expected: 1.5,
   124  		},
   125  		{
   126  			name:     "positive hours",
   127  			of:       90 * chrono.Minute,
   128  			f:        chrono.Duration.Hours,
   129  			expected: 1.5,
   130  		},
   131  		{
   132  			name:     "negative nanoseconds",
   133  			of:       -9000000000000 * chrono.Nanosecond,
   134  			f:        chrono.Duration.Nanoseconds,
   135  			expected: -9000000000000,
   136  		},
   137  		{
   138  			name:     "negative microseconds",
   139  			of:       -9500 * chrono.Nanosecond,
   140  			f:        chrono.Duration.Microseconds,
   141  			expected: -9.5,
   142  		},
   143  		{
   144  			name:     "negative milliseconds",
   145  			of:       -9500 * chrono.Microsecond,
   146  			f:        chrono.Duration.Milliseconds,
   147  			expected: -9.5,
   148  		},
   149  		{
   150  			name:     "negative seconds",
   151  			of:       -9500 * chrono.Millisecond,
   152  			f:        chrono.Duration.Seconds,
   153  			expected: -9.5,
   154  		},
   155  		{
   156  			name:     "negative minutes",
   157  			of:       -90 * chrono.Second,
   158  			f:        chrono.Duration.Minutes,
   159  			expected: -1.5,
   160  		},
   161  		{
   162  			name:     "negative hours",
   163  			of:       -90 * chrono.Minute,
   164  			f:        chrono.Duration.Hours,
   165  			expected: -1.5,
   166  		},
   167  	} {
   168  		t.Run(tt.name, func(t *testing.T) {
   169  			d := chrono.DurationOf(tt.of)
   170  			if out := tt.f(d); out != tt.expected {
   171  				t.Errorf("%v() = %f, want %f", runtime.FuncForPC(reflect.ValueOf(tt.f).Pointer()).Name(), out, tt.expected)
   172  			}
   173  		})
   174  	}
   175  }
   176  
   177  func TestDuration_Compare(t *testing.T) {
   178  	for _, tt := range []struct {
   179  		name     string
   180  		d        chrono.Duration
   181  		d2       chrono.Duration
   182  		expected int
   183  	}{
   184  		{"seconds less", chrono.DurationOf(1 * chrono.Hour), chrono.DurationOf(2 * chrono.Hour), -1},
   185  		{"seconds more", chrono.DurationOf(2 * chrono.Hour), chrono.DurationOf(1 * chrono.Hour), 1},
   186  		{"nanos less", chrono.DurationOf(1 * chrono.Nanosecond), chrono.DurationOf(2 * chrono.Nanosecond), -1},
   187  		{"nanos more", chrono.DurationOf(2 * chrono.Nanosecond), chrono.DurationOf(1 * chrono.Nanosecond), 1},
   188  		{"equal", chrono.DurationOf(chrono.Minute), chrono.DurationOf(chrono.Minute), 0},
   189  	} {
   190  		t.Run(tt.name, func(t *testing.T) {
   191  			if v := tt.d.Compare(tt.d2); v != tt.expected {
   192  				t.Errorf("d.Compare(d2) = %d, want %d", v, tt.expected)
   193  			}
   194  		})
   195  	}
   196  }
   197  
   198  func TestDuration_Add(t *testing.T) {
   199  	for _, tt := range []struct {
   200  		name     string
   201  		d1       chrono.Duration
   202  		d2       chrono.Duration
   203  		expected chrono.Duration
   204  	}{
   205  		{
   206  			name:     "add",
   207  			d1:       chrono.DurationOf((1 * chrono.Hour) + (750 * chrono.Millisecond)),
   208  			d2:       chrono.DurationOf((1 * chrono.Hour) + (550 * chrono.Millisecond)),
   209  			expected: chrono.DurationOf((2 * chrono.Hour) + (1 * chrono.Second) + (300 * chrono.Millisecond)),
   210  		},
   211  		{
   212  			name:     "add nanoseconds component",
   213  			d1:       chrono.DurationOf(750 * chrono.Millisecond),
   214  			d2:       chrono.DurationOf(550 * chrono.Millisecond),
   215  			expected: chrono.DurationOf((1 * chrono.Second) + (300 * chrono.Millisecond)),
   216  		},
   217  		{
   218  			name:     "add both components",
   219  			d1:       chrono.DurationOf((1 * chrono.Hour) + (750 * chrono.Millisecond)),
   220  			d2:       chrono.DurationOf((1 * chrono.Hour) + (550 * chrono.Millisecond)),
   221  			expected: chrono.DurationOf((2 * chrono.Hour) + (1 * chrono.Second) + (300 * chrono.Millisecond)),
   222  		},
   223  		{
   224  			name:     "minus seconds component",
   225  			d1:       chrono.DurationOf(2 * chrono.Hour),
   226  			d2:       chrono.DurationOf(-1 * chrono.Hour),
   227  			expected: chrono.DurationOf(1 * chrono.Hour),
   228  		},
   229  		{
   230  			name:     "minus nanoseconds component",
   231  			d1:       chrono.DurationOf(750 * chrono.Millisecond),
   232  			d2:       chrono.DurationOf(-550 * chrono.Millisecond),
   233  			expected: chrono.DurationOf(200 * chrono.Millisecond),
   234  		},
   235  		{
   236  			name:     "minus both components",
   237  			d1:       chrono.DurationOf((2 * chrono.Hour) + (750 * chrono.Millisecond)),
   238  			d2:       chrono.DurationOf(-((1 * chrono.Hour) + (550 * chrono.Millisecond))),
   239  			expected: chrono.DurationOf((1 * chrono.Hour) + (200 * chrono.Millisecond)),
   240  		},
   241  	} {
   242  		t.Run(tt.name, func(t *testing.T) {
   243  			t.Run("d1.Add(d2)", func(t *testing.T) {
   244  				if ok := tt.d1.CanAdd(tt.d2); !ok {
   245  					t.Error("d1.CanAdd(d2) = false, want true")
   246  				}
   247  
   248  				if added := tt.d1.Add(tt.d2); added.Compare(tt.expected) != 0 {
   249  					t.Errorf("d1.Add(d2) = %v, want %v", added, tt.expected)
   250  				}
   251  			})
   252  
   253  			t.Run("d2.Add(d1)", func(t *testing.T) {
   254  				if ok := tt.d2.CanAdd(tt.d1); !ok {
   255  					t.Error("d2.CanAdd(d1) = false, want true")
   256  				}
   257  
   258  				if added := tt.d2.Add(tt.d1); added.Compare(tt.expected) != 0 {
   259  					t.Errorf("d2.Add(d1) = %v, want %v", added, tt.expected)
   260  				}
   261  			})
   262  		})
   263  	}
   264  
   265  	for _, tt := range []struct {
   266  		name string
   267  		d1   chrono.Duration
   268  		d2   chrono.Duration
   269  	}{
   270  		{
   271  			name: "overflow",
   272  			d1:   chrono.MaxDuration(),
   273  			d2:   chrono.DurationOf(1 * chrono.Nanosecond),
   274  		},
   275  		{
   276  			name: "underflow",
   277  			d1:   chrono.MinDuration(),
   278  			d2:   chrono.DurationOf(-1 * chrono.Nanosecond),
   279  		},
   280  	} {
   281  		t.Run(tt.name, func(t *testing.T) {
   282  			t.Run("d1.Add(d2)", func(t *testing.T) {
   283  				if ok := tt.d1.CanAdd(tt.d2); ok {
   284  					t.Error("d1.CanAdd(d2) = true, want false")
   285  				}
   286  
   287  				func() {
   288  					defer func() {
   289  						if r := recover(); r == nil {
   290  							t.Error("expecting panic that didn't occur")
   291  						}
   292  					}()
   293  
   294  					tt.d1.Add(tt.d2)
   295  				}()
   296  			})
   297  
   298  			t.Run("d2.Add(d1)", func(t *testing.T) {
   299  				if ok := tt.d2.CanAdd(tt.d1); ok {
   300  					t.Error("d2.CanAdd(d1) = true, want false")
   301  				}
   302  
   303  				func() {
   304  					defer func() {
   305  						if r := recover(); r == nil {
   306  							t.Error("expecting panic that didn't occur")
   307  						}
   308  					}()
   309  
   310  					tt.d2.Add(tt.d1)
   311  				}()
   312  			})
   313  		})
   314  	}
   315  }
   316  
   317  func TestDuration_Format(t *testing.T) {
   318  	for _, tt := range formatDurationCases {
   319  		t.Run(tt.name, func(t *testing.T) {
   320  			t.Run("positive", func(t *testing.T) {
   321  				d := chrono.DurationOf(tt.of)
   322  				if out := d.Format(tt.exclusive...); out != tt.expected {
   323  					t.Errorf("formatted duration = %s, want %s", out, tt.expected)
   324  				}
   325  			})
   326  
   327  			t.Run("negative", func(t *testing.T) {
   328  				expected := tt.expected
   329  				if tt.of != 0 {
   330  					expected = "-" + expected
   331  				}
   332  
   333  				d := chrono.DurationOf(tt.of * -1)
   334  				if out := d.Format(tt.exclusive...); out != expected {
   335  					t.Errorf("formatted duration = %s, want %s", out, expected)
   336  				}
   337  			})
   338  		})
   339  	}
   340  }
   341  
   342  func TestDuration_Parse(t *testing.T) {
   343  	for _, tt := range parseDurationCases {
   344  		t.Run(tt.name, func(t *testing.T) {
   345  			for _, sign := range []string{"", "+", "-"} {
   346  				t.Run(sign, func(t *testing.T) {
   347  					input := sign + tt.input
   348  					expected := tt.expected
   349  					if sign == "-" {
   350  						expected *= -1
   351  					}
   352  
   353  					run := func() {
   354  						var d chrono.Duration
   355  						if err := d.Parse(input); err != nil {
   356  							t.Errorf("failed to parse duation: %v", err)
   357  						} else if d.Compare(chrono.DurationOf(expected)) != 0 {
   358  							t.Errorf("parsed duration = %v, want %v", d, expected)
   359  						}
   360  					}
   361  
   362  					t.Run("dots", func(t *testing.T) {
   363  						run()
   364  					})
   365  
   366  					t.Run("commas", func(t *testing.T) {
   367  						tt.input = strings.ReplaceAll(tt.input, ".", ",")
   368  						run()
   369  					})
   370  				})
   371  			}
   372  		})
   373  	}
   374  
   375  	t.Run("overflows", func(t *testing.T) {
   376  		var d chrono.Duration
   377  		if err := d.Parse("PT2562047788015216H"); err == nil {
   378  			t.Error("expecting error but got nil")
   379  		}
   380  	})
   381  
   382  	t.Run("underflows", func(t *testing.T) {
   383  		var d chrono.Duration
   384  		if err := d.Parse("PT-2562047788015215H"); err == nil {
   385  			t.Error("expecting error but got nil")
   386  		}
   387  	})
   388  }
   389  
   390  func TestDuration_Units(t *testing.T) {
   391  	d := chrono.DurationOf(12*chrono.Hour + 34*chrono.Minute + 56*chrono.Second + 7*chrono.Nanosecond)
   392  
   393  	hours, mins, secs, nsec := d.Units()
   394  	if hours != 12 {
   395  		t.Errorf("expecting 12 hours, got %d", hours)
   396  	}
   397  
   398  	if mins != 34 {
   399  		t.Errorf("expecting 34 mins, got %d", mins)
   400  	}
   401  
   402  	if secs != 56 {
   403  		t.Errorf("expecting 56 secs, got %d", secs)
   404  	}
   405  
   406  	if nsec != 7 {
   407  		t.Errorf("expecting 7 nsecs, got %d", nsec)
   408  	}
   409  }