github.com/vugu/vugu@v0.3.6-0.20240430171613-3f6f402e014b/mod-check_test.go (about)

     1  package vugu
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  )
     9  
    10  func TestModCheckerWidget(t *testing.T) {
    11  
    12  	// try a more complex but realistic example
    13  
    14  	assert := assert.New(t)
    15  	mt := NewModTracker()
    16  
    17  	type Tag struct {
    18  		Name  string `vugu:"data" json:"name"`
    19  		Style int    `vugu:"data" json:"style"`
    20  	}
    21  
    22  	type Widget struct {
    23  		ID           int64   `vugu:"data" json:"id"`
    24  		Name         string  `vugu:"data" json:"name"`
    25  		Description  *string `vugu:"data" json:"description"`
    26  		Active       bool    `vugu:"data" json:"active"`
    27  		StockCount   *int32  `vugu:"data" json:"stock_count"`
    28  		Tags         []Tag   `vugu:"data" json:"tags"`
    29  		FeaturedTags []*Tag  `vugu:"data" json:"-"` // calculated separately after loading
    30  	}
    31  
    32  	wjson := `
    33  	[
    34  		{
    35  			"id": 100,
    36  			"name": "Widgetron",
    37  			"description": "This one is electronical",
    38  			"active": true,
    39  			"stock_count": 42,
    40  			"tags": [{"name":"Electronical","style":314159},{"name":"Whatever","style":1}]
    41  		},
    42  		{
    43  			"id": 101,
    44  			"name": "Widgetcron",
    45  			"description": "This one is chronical",
    46  			"active": true,
    47  			"stock_count": 2400,
    48  			"tags": [{"name":"Something","style":2400},{"name":"Whatever","style":1}]
    49  		},
    50  		{
    51  			"id": 102,
    52  			"name": "Widgetevil",
    53  			"description": "This one is diabolical",
    54  			"active": false,
    55  			"stock_count": 13,
    56  			"tags": [{"name":"Something","style":666}]
    57  		}
    58  	]
    59  	`
    60  
    61  	var wlist []Widget
    62  	assert.NoError(json.Unmarshal([]byte(wjson), &wlist))
    63  
    64  	// log.Printf("wlist: %#v", wlist)
    65  
    66  	mt.TrackNext()
    67  	assert.True(mt.ModCheckAll(&wlist))
    68  	assert.True(mt.ModCheckAll(&wlist))
    69  
    70  	mt.TrackNext()
    71  	assert.False(mt.ModCheckAll(&wlist))
    72  
    73  	// change the sequence
    74  	mt.TrackNext()
    75  	wlist[0], wlist[1] = wlist[1], wlist[0]
    76  	assert.True(mt.ModCheckAll(&wlist))
    77  
    78  	mt.TrackNext()
    79  	assert.False(mt.ModCheckAll(&wlist))
    80  
    81  	// change a field value
    82  	mt.TrackNext()
    83  	wlist[0].Name = "Widgetcroninator"
    84  	assert.True(mt.ModCheckAll(&wlist))
    85  
    86  	mt.TrackNext()
    87  	assert.False(mt.ModCheckAll(&wlist))
    88  
    89  	// change a pointer field
    90  	mt.TrackNext()
    91  	newStockCount := int32(2401)
    92  	wlist[0].StockCount = &newStockCount
    93  	assert.True(mt.ModCheckAll(&wlist))
    94  
    95  	mt.TrackNext()
    96  	assert.False(mt.ModCheckAll(&wlist))
    97  
    98  	// log.Printf("dumpA: %s", mt.dump())
    99  
   100  	// change the value pointed to by a field
   101  	mt.TrackNext()
   102  	*wlist[0].StockCount = 2402
   103  	assert.True(mt.ModCheckAll(&wlist))
   104  
   105  	// log.Printf("dumpB: %s", mt.dump())
   106  
   107  	mt.TrackNext()
   108  	assert.False(mt.ModCheckAll(&wlist))
   109  
   110  	// set pointer field to nil
   111  	mt.TrackNext()
   112  	wlist[0].StockCount = nil
   113  	assert.True(mt.ModCheckAll(&wlist))
   114  
   115  	mt.TrackNext()
   116  	assert.False(mt.ModCheckAll(&wlist))
   117  
   118  	// log.Printf("dump: %s", mt.dump())
   119  
   120  	// now try it with a slice of struct pointers
   121  	var wplist []*Widget
   122  	assert.NoError(json.Unmarshal([]byte(wjson), &wplist))
   123  
   124  	mt.TrackNext()
   125  	assert.True(mt.ModCheckAll(&wplist))
   126  	assert.True(mt.ModCheckAll(&wplist))
   127  
   128  	mt.TrackNext()
   129  	assert.False(mt.ModCheckAll(&wplist))
   130  
   131  	// change the sequence
   132  	mt.TrackNext()
   133  	wplist[0], wplist[1] = wplist[1], wplist[0]
   134  	assert.True(mt.ModCheckAll(&wplist))
   135  
   136  	mt.TrackNext()
   137  	assert.False(mt.ModCheckAll(&wplist))
   138  
   139  	// change a field value
   140  	mt.TrackNext()
   141  	wplist[0].Name = "Widgetcroninator"
   142  	assert.True(mt.ModCheckAll(&wplist))
   143  
   144  	mt.TrackNext()
   145  	assert.False(mt.ModCheckAll(&wplist))
   146  
   147  	// change a pointer field
   148  	mt.TrackNext()
   149  	newStockCount = int32(2401)
   150  	wplist[0].StockCount = &newStockCount
   151  	assert.True(mt.ModCheckAll(&wplist))
   152  
   153  	mt.TrackNext()
   154  	assert.False(mt.ModCheckAll(&wplist))
   155  
   156  	// log.Printf("dumpA: %s", mt.dump())
   157  
   158  	// change the value pointed to by a field
   159  	mt.TrackNext()
   160  	*wplist[0].StockCount = 2402
   161  	assert.True(mt.ModCheckAll(&wplist))
   162  
   163  	// log.Printf("dumpB: %s", mt.dump())
   164  
   165  	mt.TrackNext()
   166  	assert.False(mt.ModCheckAll(&wplist))
   167  
   168  	// set pointer field to nil
   169  	mt.TrackNext()
   170  	wplist[0].StockCount = nil
   171  	assert.True(mt.ModCheckAll(&wplist))
   172  
   173  	mt.TrackNext()
   174  	assert.False(mt.ModCheckAll(&wplist))
   175  
   176  	// set one of the struct elements to nil
   177  	mt.TrackNext()
   178  	wplist[0] = nil
   179  	assert.True(mt.ModCheckAll(&wplist))
   180  
   181  	mt.TrackNext()
   182  	assert.False(mt.ModCheckAll(&wplist))
   183  
   184  }
   185  
   186  func TestModCheckerStruct(t *testing.T) {
   187  	assert := assert.New(t)
   188  	mt := NewModTracker()
   189  
   190  	var s1 struct {
   191  		F1 string  `vugu:"data"`
   192  		F2 int     `vugu:"data"`
   193  		F3 float64 // not tagged
   194  	}
   195  
   196  	mt.TrackNext()
   197  	assert.True(mt.ModCheckAll(&s1))
   198  
   199  	mt.TrackNext()
   200  	assert.False(mt.ModCheckAll(&s1))
   201  
   202  	mt.TrackNext()
   203  	s1.F1 = "test1"
   204  	assert.True(mt.ModCheckAll(&s1))
   205  
   206  	mt.TrackNext()
   207  	s1.F2 = 1
   208  	assert.True(mt.ModCheckAll(&s1))
   209  
   210  	mt.TrackNext()
   211  	s1.F3 = 1.0 // field not tagged, should not cause modification
   212  	assert.False(mt.ModCheckAll(&s1))
   213  
   214  }
   215  
   216  func TestModCheckerSliceArray(t *testing.T) {
   217  
   218  	assert := assert.New(t)
   219  	mt := NewModTracker()
   220  
   221  	var a1 [3]string
   222  	a1[0] = "s1"
   223  	a1[1] = "s2"
   224  	a1[2] = "s3"
   225  
   226  	assert.True(mt.ModCheckAll(&a1))
   227  
   228  	// log.Printf("state1: \n%s", mt.dump())
   229  
   230  	mt.TrackNext()
   231  
   232  	// log.Printf("state2: \n%s", mt.dump())
   233  
   234  	assert.False(mt.ModCheckAll(&a1))
   235  
   236  	// log.Printf("state3: \n%s", mt.dump())
   237  
   238  	var s1 []string
   239  	s1 = a1[:]
   240  
   241  	mt.TrackNext()
   242  	assert.True(mt.ModCheckAll(&s1))
   243  
   244  	mt.TrackNext()
   245  	assert.False(mt.ModCheckAll(&s1))
   246  
   247  	mt.TrackNext()
   248  	s1 = a1[:2]
   249  	assert.True(mt.ModCheckAll(&s1))
   250  
   251  	// swap two elements
   252  	mt.TrackNext()
   253  	s1[0], s1[1] = s1[1], s1[0]
   254  	assert.True(mt.ModCheckAll(&s1))
   255  
   256  	mt.TrackNext()
   257  	assert.False(mt.ModCheckAll(&s1))
   258  
   259  }
   260  
   261  func TestModCheckerStrings(t *testing.T) {
   262  
   263  	assert := assert.New(t)
   264  
   265  	var mt ModTracker
   266  
   267  	mt.TrackNext()
   268  	s := "testing" // initial value
   269  	assert.True(mt.ModCheckAll(&s))
   270  
   271  	mt.TrackNext()
   272  	// no change
   273  	assert.False(mt.ModCheckAll(&s))
   274  
   275  	mt.TrackNext()
   276  	s = "testing2" // different value
   277  	assert.True(mt.ModCheckAll(&s))
   278  
   279  	mt.TrackNext()
   280  	s = "testing" // back to earlier value
   281  	assert.True(mt.ModCheckAll(&s))
   282  
   283  	mt.TrackNext()
   284  	s = "testing" // same value
   285  	assert.False(mt.ModCheckAll(&s))
   286  
   287  	// run through it again with a byte slice
   288  
   289  	mt.TrackNext()
   290  	b := []byte("testing") // initial value
   291  	assert.True(mt.ModCheckAll(&b))
   292  
   293  	mt.TrackNext()
   294  	// no change
   295  	assert.False(mt.ModCheckAll(&b))
   296  
   297  	mt.TrackNext()
   298  	b = []byte("testing2") // different value
   299  	assert.True(mt.ModCheckAll(&b))
   300  
   301  	mt.TrackNext()
   302  	b = []byte("testing") // back to earlier value
   303  	assert.True(mt.ModCheckAll(&b))
   304  
   305  	mt.TrackNext()
   306  	b = []byte("testing") // same value
   307  	assert.False(mt.ModCheckAll(&b))
   308  
   309  	// check both
   310  	mt.TrackNext()
   311  
   312  	mt.TrackNext()
   313  	b = []byte("testing")
   314  	assert.True(mt.ModCheckAll(&b))
   315  	s = "testing"
   316  	assert.True(mt.ModCheckAll(&s))
   317  
   318  	mt.TrackNext()
   319  	b = []byte("testing")
   320  	assert.False(mt.ModCheckAll(&b))
   321  	s = "testing"
   322  	assert.False(mt.ModCheckAll(&s))
   323  
   324  }
   325  
   326  func TestModCheckerBool(t *testing.T) {
   327  
   328  	mt := NewModTracker()
   329  
   330  	var v1, v2 bool
   331  	check := func(vp *bool, newv bool, expectedMod bool) {
   332  		mt.TrackNext()
   333  		*vp = newv
   334  		mod := mt.ModCheckAll(vp)
   335  		if mod != expectedMod {
   336  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   337  		}
   338  	}
   339  
   340  	check(&v1, false, true)
   341  	check(&v1, false, false)
   342  	check(&v1, true, true)
   343  	check(&v1, true, false)
   344  	check(&v1, false, true)
   345  	check(&v1, false, false)
   346  	check(&v1, false, false)
   347  	check(&v2, false, true)
   348  	check(&v2, true, true)
   349  	check(&v2, false, true)
   350  	check(&v2, false, false)
   351  
   352  }
   353  
   354  func TestModCheckerInt(t *testing.T) {
   355  
   356  	mt := NewModTracker()
   357  
   358  	var v1, v2 int
   359  	check := func(vp *int, newv int, expectedMod bool) {
   360  		mt.TrackNext()
   361  		*vp = newv
   362  		mod := mt.ModCheckAll(vp)
   363  		if mod != expectedMod {
   364  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   365  		}
   366  	}
   367  
   368  	check(&v1, 1, true)
   369  	check(&v1, 1, false)
   370  	check(&v1, 2, true)
   371  	check(&v1, 2, false)
   372  	check(&v1, 1, true)
   373  	check(&v1, 1, false)
   374  	check(&v1, 1, false)
   375  	check(&v2, 1, true)
   376  	check(&v2, 2, true)
   377  	check(&v2, 1, true)
   378  	check(&v2, 1, false)
   379  
   380  }
   381  
   382  func TestModCheckerInt8(t *testing.T) {
   383  
   384  	mt := NewModTracker()
   385  
   386  	var v1, v2 int8
   387  	check := func(vp *int8, newv int8, expectedMod bool) {
   388  		mt.TrackNext()
   389  		*vp = newv
   390  		mod := mt.ModCheckAll(vp)
   391  		if mod != expectedMod {
   392  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   393  		}
   394  	}
   395  
   396  	check(&v1, 1, true)
   397  	check(&v1, 1, false)
   398  	check(&v1, 2, true)
   399  	check(&v1, 2, false)
   400  	check(&v1, 1, true)
   401  	check(&v1, 1, false)
   402  	check(&v1, 1, false)
   403  	check(&v2, 1, true)
   404  	check(&v2, 2, true)
   405  	check(&v2, 1, true)
   406  	check(&v2, 1, false)
   407  
   408  }
   409  
   410  func TestModCheckerInt16(t *testing.T) {
   411  
   412  	mt := NewModTracker()
   413  
   414  	var v1, v2 int16
   415  	check := func(vp *int16, newv int16, expectedMod bool) {
   416  		mt.TrackNext()
   417  		*vp = newv
   418  		mod := mt.ModCheckAll(vp)
   419  		if mod != expectedMod {
   420  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   421  		}
   422  	}
   423  
   424  	check(&v1, 1, true)
   425  	check(&v1, 1, false)
   426  	check(&v1, 2, true)
   427  	check(&v1, 2, false)
   428  	check(&v1, 1, true)
   429  	check(&v1, 1, false)
   430  	check(&v1, 1, false)
   431  	check(&v2, 1, true)
   432  	check(&v2, 2, true)
   433  	check(&v2, 1, true)
   434  	check(&v2, 1, false)
   435  
   436  }
   437  
   438  func TestModCheckerInt32(t *testing.T) {
   439  
   440  	mt := NewModTracker()
   441  
   442  	var v1, v2 int32
   443  	check := func(vp *int32, newv int32, expectedMod bool) {
   444  		mt.TrackNext()
   445  		*vp = newv
   446  		mod := mt.ModCheckAll(vp)
   447  		if mod != expectedMod {
   448  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   449  		}
   450  	}
   451  
   452  	check(&v1, 1, true)
   453  	check(&v1, 1, false)
   454  	check(&v1, 2, true)
   455  	check(&v1, 2, false)
   456  	check(&v1, 1, true)
   457  	check(&v1, 1, false)
   458  	check(&v1, 1, false)
   459  	check(&v2, 1, true)
   460  	check(&v2, 2, true)
   461  	check(&v2, 1, true)
   462  	check(&v2, 1, false)
   463  
   464  }
   465  
   466  func TestModCheckerInt64(t *testing.T) {
   467  
   468  	mt := NewModTracker()
   469  
   470  	var v1, v2 int64
   471  	check := func(vp *int64, newv int64, expectedMod bool) {
   472  		mt.TrackNext()
   473  		*vp = newv
   474  		mod := mt.ModCheckAll(vp)
   475  		if mod != expectedMod {
   476  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   477  		}
   478  	}
   479  
   480  	check(&v1, 1, true)
   481  	check(&v1, 1, false)
   482  	check(&v1, 2, true)
   483  	check(&v1, 2, false)
   484  	check(&v1, 1, true)
   485  	check(&v1, 1, false)
   486  	check(&v1, 1, false)
   487  	check(&v2, 1, true)
   488  	check(&v2, 2, true)
   489  	check(&v2, 1, true)
   490  	check(&v2, 1, false)
   491  
   492  }
   493  
   494  func TestModCheckerUint(t *testing.T) {
   495  
   496  	mt := NewModTracker()
   497  
   498  	var v1, v2 uint
   499  	check := func(vp *uint, newv uint, expectedMod bool) {
   500  		mt.TrackNext()
   501  		*vp = newv
   502  		mod := mt.ModCheckAll(vp)
   503  		if mod != expectedMod {
   504  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   505  		}
   506  	}
   507  
   508  	check(&v1, 1, true)
   509  	check(&v1, 1, false)
   510  	check(&v1, 2, true)
   511  	check(&v1, 2, false)
   512  	check(&v1, 1, true)
   513  	check(&v1, 1, false)
   514  	check(&v1, 1, false)
   515  	check(&v2, 1, true)
   516  	check(&v2, 2, true)
   517  	check(&v2, 1, true)
   518  	check(&v2, 1, false)
   519  
   520  }
   521  
   522  func TestModCheckerUint8(t *testing.T) {
   523  
   524  	mt := NewModTracker()
   525  
   526  	var v1, v2 uint8
   527  	check := func(vp *uint8, newv uint8, expectedMod bool) {
   528  		mt.TrackNext()
   529  		*vp = newv
   530  		mod := mt.ModCheckAll(vp)
   531  		if mod != expectedMod {
   532  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   533  		}
   534  	}
   535  
   536  	check(&v1, 1, true)
   537  	check(&v1, 1, false)
   538  	check(&v1, 2, true)
   539  	check(&v1, 2, false)
   540  	check(&v1, 1, true)
   541  	check(&v1, 1, false)
   542  	check(&v1, 1, false)
   543  	check(&v2, 1, true)
   544  	check(&v2, 2, true)
   545  	check(&v2, 1, true)
   546  	check(&v2, 1, false)
   547  
   548  }
   549  
   550  func TestModCheckerUint16(t *testing.T) {
   551  
   552  	mt := NewModTracker()
   553  
   554  	var v1, v2 uint16
   555  	check := func(vp *uint16, newv uint16, expectedMod bool) {
   556  		mt.TrackNext()
   557  		*vp = newv
   558  		mod := mt.ModCheckAll(vp)
   559  		if mod != expectedMod {
   560  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   561  		}
   562  	}
   563  
   564  	check(&v1, 1, true)
   565  	check(&v1, 1, false)
   566  	check(&v1, 2, true)
   567  	check(&v1, 2, false)
   568  	check(&v1, 1, true)
   569  	check(&v1, 1, false)
   570  	check(&v1, 1, false)
   571  	check(&v2, 1, true)
   572  	check(&v2, 2, true)
   573  	check(&v2, 1, true)
   574  	check(&v2, 1, false)
   575  
   576  }
   577  
   578  func TestModCheckerUint32(t *testing.T) {
   579  
   580  	mt := NewModTracker()
   581  
   582  	var v1, v2 uint32
   583  	check := func(vp *uint32, newv uint32, expectedMod bool) {
   584  		mt.TrackNext()
   585  		*vp = newv
   586  		mod := mt.ModCheckAll(vp)
   587  		if mod != expectedMod {
   588  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   589  		}
   590  	}
   591  
   592  	check(&v1, 1, true)
   593  	check(&v1, 1, false)
   594  	check(&v1, 2, true)
   595  	check(&v1, 2, false)
   596  	check(&v1, 1, true)
   597  	check(&v1, 1, false)
   598  	check(&v1, 1, false)
   599  	check(&v2, 1, true)
   600  	check(&v2, 2, true)
   601  	check(&v2, 1, true)
   602  	check(&v2, 1, false)
   603  
   604  }
   605  
   606  func TestModCheckerUint64(t *testing.T) {
   607  
   608  	mt := NewModTracker()
   609  
   610  	var v1, v2 uint64
   611  	check := func(vp *uint64, newv uint64, expectedMod bool) {
   612  		mt.TrackNext()
   613  		*vp = newv
   614  		mod := mt.ModCheckAll(vp)
   615  		if mod != expectedMod {
   616  			t.Errorf("check(%#v, %#v, %#v) wrong mod result: %v", vp, newv, expectedMod, mod)
   617  		}
   618  	}
   619  
   620  	check(&v1, 1, true)
   621  	check(&v1, 1, false)
   622  	check(&v1, 2, true)
   623  	check(&v1, 2, false)
   624  	check(&v1, 1, true)
   625  	check(&v1, 1, false)
   626  	check(&v1, 1, false)
   627  	check(&v2, 1, true)
   628  	check(&v2, 2, true)
   629  	check(&v2, 1, true)
   630  	check(&v2, 1, false)
   631  
   632  }