github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/cmn/cos/template_test.go (about)

     1  // Package test provides tests for common low-level types and utilities for all aistore projects
     2  /*
     3   * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package cos_test
     6  
     7  import (
     8  	"math"
     9  
    10  	"github.com/NVIDIA/aistore/cmn/cos"
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  )
    14  
    15  var _ = Describe("Template", func() {
    16  	Context("ParseFmtTemplate", func() {
    17  		DescribeTable("parse fmt template without error",
    18  			func(template string, expectedPt cos.ParsedTemplate) {
    19  				pt, err := cos.ParseFmtTemplate(template)
    20  				Expect(err).ShouldNot(HaveOccurred())
    21  				Expect(pt).To(Equal(expectedPt))
    22  			},
    23  			Entry("simple", "%d", cos.ParsedTemplate{
    24  				Prefix: "",
    25  				Ranges: []cos.TemplateRange{{
    26  					Start:      0,
    27  					End:        math.MaxInt64 - 1,
    28  					Step:       1,
    29  					DigitCount: 0,
    30  					Gap:        "",
    31  				}},
    32  			}),
    33  			Entry("with prefix", "prefix-%d", cos.ParsedTemplate{
    34  				Prefix: "prefix-",
    35  				Ranges: []cos.TemplateRange{{
    36  					Start:      0,
    37  					End:        math.MaxInt64 - 1,
    38  					Step:       1,
    39  					DigitCount: 0,
    40  					Gap:        "",
    41  				}},
    42  			}),
    43  			Entry("with prefix and suffix", "prefix-%d-suffix", cos.ParsedTemplate{
    44  				Prefix: "prefix-",
    45  				Ranges: []cos.TemplateRange{{
    46  					Start:      0,
    47  					End:        math.MaxInt64 - 1,
    48  					Step:       1,
    49  					DigitCount: 0,
    50  					Gap:        "-suffix",
    51  				}},
    52  			}),
    53  			Entry("with 0 digits", "prefix-%00d-suffix", cos.ParsedTemplate{
    54  				Prefix: "prefix-",
    55  				Ranges: []cos.TemplateRange{{
    56  					Start:      0,
    57  					End:        math.MaxInt64 - 1,
    58  					Step:       1,
    59  					DigitCount: 0,
    60  					Gap:        "-suffix",
    61  				}},
    62  			}),
    63  			Entry("with multiple digits", "prefix-%06d-suffix", cos.ParsedTemplate{
    64  				Prefix: "prefix-",
    65  				Ranges: []cos.TemplateRange{{
    66  					Start:      0,
    67  					End:        math.MaxInt64 - 1,
    68  					Step:       1,
    69  					DigitCount: 6,
    70  					Gap:        "-suffix",
    71  				}},
    72  			}),
    73  			Entry("with large number of digits", "prefix-%0152d-suffix", cos.ParsedTemplate{
    74  				Prefix: "prefix-",
    75  				Ranges: []cos.TemplateRange{{
    76  					Start:      0,
    77  					End:        math.MaxInt64 - 1,
    78  					Step:       1,
    79  					DigitCount: 152,
    80  					Gap:        "-suffix",
    81  				}},
    82  			}),
    83  		)
    84  
    85  		DescribeTable("parse fmt template with error",
    86  			func(template string) {
    87  				_, err := cos.ParseFmtTemplate(template)
    88  				Expect(err).Should(HaveOccurred())
    89  			},
    90  			Entry("missing %", "prefix-06d-suffix"),
    91  			Entry("missing d", "prefix-%06-suffix"),
    92  			Entry("% after d", "prefix-d%06-suffix"),
    93  			Entry("negative digits", "prefix-%0-6d-suffix"),
    94  			Entry("no digits specified", "prefix-%0d-suffix"),
    95  			Entry("no zero specified", "prefix-%6d-suffix"),
    96  			// Currently rejecting even if looks quite OK.
    97  			Entry("second %", "prefix-%06d-%"),
    98  			Entry("double %%", "prefix-%%06d-suffix"),
    99  			// Currently rejecting even if the second format is not used.
   100  			Entry("second format string", "prefix-%06d-%06d"),
   101  		)
   102  	})
   103  
   104  	Context("ParseBashTemplate", func() {
   105  		DescribeTable("parse bash template without error",
   106  			func(template string, expectedPt cos.ParsedTemplate) {
   107  				pt, err := cos.ParseBashTemplate(template)
   108  				Expect(err).ShouldNot(HaveOccurred())
   109  				Expect(pt).To(Equal(expectedPt))
   110  			},
   111  			Entry("with step", "prefix-{0010..0111..2}-suffix", cos.ParsedTemplate{
   112  				Prefix: "prefix-",
   113  				Ranges: []cos.TemplateRange{{
   114  					Start:      10,
   115  					End:        111,
   116  					Step:       2,
   117  					DigitCount: 4,
   118  					Gap:        "-suffix",
   119  				}},
   120  			}),
   121  			Entry("without step and suffix", "prefix-{0010..0111}", cos.ParsedTemplate{
   122  				Prefix: "prefix-",
   123  				Ranges: []cos.TemplateRange{{
   124  					Start:      10,
   125  					End:        111,
   126  					Step:       1,
   127  					DigitCount: 4,
   128  					Gap:        "",
   129  				}},
   130  			}),
   131  			Entry("minimal", "{1..2}", cos.ParsedTemplate{
   132  				Prefix: "",
   133  				Ranges: []cos.TemplateRange{{
   134  					Start:      1,
   135  					End:        2,
   136  					Step:       1,
   137  					DigitCount: 1,
   138  					Gap:        "",
   139  				}},
   140  			}),
   141  			Entry("minimal multiple digits", "{110..220..10}", cos.ParsedTemplate{
   142  				Prefix: "",
   143  				Ranges: []cos.TemplateRange{{
   144  					Start:      110,
   145  					End:        220,
   146  					Step:       10,
   147  					DigitCount: 3,
   148  					Gap:        "",
   149  				}},
   150  			}),
   151  			Entry("minimal with digit count", "{1..02}", cos.ParsedTemplate{
   152  				Prefix: "",
   153  				Ranges: []cos.TemplateRange{{
   154  					Start:      1,
   155  					End:        2,
   156  					Step:       1,
   157  					DigitCount: 1,
   158  					Gap:        "",
   159  				}},
   160  			}),
   161  			Entry("minimal with special suffix", "{1..02}}", cos.ParsedTemplate{
   162  				Prefix: "",
   163  				Ranges: []cos.TemplateRange{{
   164  					Start:      1,
   165  					End:        2,
   166  					Step:       1,
   167  					DigitCount: 1,
   168  					Gap:        "}",
   169  				}},
   170  			}),
   171  			Entry("multi-range", "prefix-{0010..0111..2}-gap-{10..12}-gap2-{0040..0099..4}-suffix", cos.ParsedTemplate{
   172  				Prefix: "prefix-",
   173  				Ranges: []cos.TemplateRange{
   174  					{
   175  						Start:      10,
   176  						End:        111,
   177  						Step:       2,
   178  						DigitCount: 4,
   179  						Gap:        "-gap-",
   180  					},
   181  					{
   182  						Start:      10,
   183  						End:        12,
   184  						Step:       1,
   185  						DigitCount: 2,
   186  						Gap:        "-gap2-",
   187  					},
   188  					{
   189  						Start:      40,
   190  						End:        99,
   191  						Step:       4,
   192  						DigitCount: 4,
   193  						Gap:        "-suffix",
   194  					},
   195  				},
   196  			}),
   197  		)
   198  
   199  		DescribeTable("parse bash template with error",
   200  			func(template string) {
   201  				_, err := cos.ParseBashTemplate(template)
   202  				Expect(err).Should(HaveOccurred())
   203  			},
   204  			Entry("missing {", "prefix-0010..0111..2}-suffix"),
   205  			Entry("missing }", "prefix-{001..009-suffix"),
   206  			Entry("start after end", "prefix-{002..001}-suffix"),
   207  			Entry("negative start", "prefix-{-001..009}-suffix"),
   208  			Entry("negative step", "prefix-{001..009..-1}-suffix"),
   209  			Entry("invalid step", "prefix-{0010..0111..2s}-suffix"),
   210  			Entry("invalid end", "prefix-{0010..0111s..2}-suffix"),
   211  			Entry("invalid start", "prefix-{0010s..0111..2}-suffix"),
   212  			Entry("too many arms", "prefix-{00..10..0111..2}-suffix"),
   213  			Entry("missing end", "prefix-{010..}-suffix"),
   214  			Entry("missing start", "prefix-{..009}-suffix"),
   215  			Entry("missing start and end", "prefix-{..}-suffix"),
   216  			Entry("empty template with prefix and suffix", "prefix-{}-suffix"),
   217  			Entry("empty template", "{}"),
   218  			Entry("nested templates", "{{}}"),
   219  			Entry("nested templates with numbers", "{{1..2}}"),
   220  			Entry("interleaving templates", "{1..2{3..4}}"),
   221  		)
   222  	})
   223  
   224  	Context("ParseAtTemplate", func() {
   225  		DescribeTable("parse at template without error",
   226  			func(template string, expectedPt cos.ParsedTemplate) {
   227  				pt, err := cos.ParseAtTemplate(template)
   228  				Expect(err).ShouldNot(HaveOccurred())
   229  				Expect(pt).To(Equal(expectedPt))
   230  			},
   231  			Entry("full featured template", "prefix-@010-suffix", cos.ParsedTemplate{
   232  				Prefix: "prefix-",
   233  				Ranges: []cos.TemplateRange{{
   234  					Start:      0,
   235  					End:        10,
   236  					Step:       1,
   237  					DigitCount: 3,
   238  					Gap:        "-suffix",
   239  				}},
   240  			}),
   241  			Entry("minimal with prefix", "pref@9", cos.ParsedTemplate{
   242  				Prefix: "pref",
   243  				Ranges: []cos.TemplateRange{{
   244  					Start:      0,
   245  					End:        9,
   246  					Step:       1,
   247  					DigitCount: 1,
   248  					Gap:        "",
   249  				}},
   250  			}),
   251  			Entry("minimal", "@0010", cos.ParsedTemplate{
   252  				Prefix: "",
   253  				Ranges: []cos.TemplateRange{{
   254  					Start:      0,
   255  					End:        10,
   256  					Step:       1,
   257  					DigitCount: 4,
   258  					Gap:        "",
   259  				}},
   260  			}),
   261  			Entry("multi-range", "pref@9-gap-@0100-suffix", cos.ParsedTemplate{
   262  				Prefix: "pref",
   263  				Ranges: []cos.TemplateRange{
   264  					{
   265  						Start:      0,
   266  						End:        9,
   267  						Step:       1,
   268  						DigitCount: 1,
   269  						Gap:        "-gap-",
   270  					},
   271  					{
   272  						Start:      0,
   273  						End:        100,
   274  						Step:       1,
   275  						DigitCount: 4,
   276  						Gap:        "-suffix",
   277  					},
   278  				},
   279  			}),
   280  		)
   281  
   282  		DescribeTable("parse at template with error",
   283  			func(template string) {
   284  				_, err := cos.ParseAtTemplate(template)
   285  				Expect(err).Should(HaveOccurred())
   286  			},
   287  			Entry("missing @", "prefix-01-suffix"),
   288  			Entry("negative end", "prefix-@-0001-suffix"),
   289  			Entry("repetitive @", "prefix-@@0010-suffix"),
   290  			Entry("non-digit range", "prefix-@d@0010-suffix"),
   291  		)
   292  	})
   293  
   294  	Context("ParsedTemplate", func() {
   295  		DescribeTable("iter method",
   296  			func(template string, expectedStrs ...string) {
   297  				pt, err := cos.ParseBashTemplate(template)
   298  				Expect(err).NotTo(HaveOccurred())
   299  
   300  				var i int
   301  				pt.InitIter()
   302  				for str, hasNext := pt.Next(); hasNext; str, hasNext = pt.Next() {
   303  					Expect(str).To(Equal(expectedStrs[i]))
   304  					i++
   305  				}
   306  				Expect(i).To(Equal(len(expectedStrs)))
   307  				Expect(i).To(Equal(int(pt.Count())))
   308  			},
   309  			Entry(
   310  				"simple template", "prefix-{0010..0013..2}-suffix",
   311  				"prefix-0010-suffix", "prefix-0012-suffix",
   312  			),
   313  			Entry(
   314  				"multi-range template", "prefix-{0010..0013..2}-gap-{1..2}-suffix",
   315  				"prefix-0010-gap-1-suffix", "prefix-0010-gap-2-suffix", "prefix-0012-gap-1-suffix", "prefix-0012-gap-2-suffix",
   316  			),
   317  			Entry(
   318  				"large step", "prefix-{0010..0013..2}-gap-{1..2..3}-suffix",
   319  				"prefix-0010-gap-1-suffix", "prefix-0012-gap-1-suffix",
   320  			),
   321  		)
   322  	})
   323  })