github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/binlog/position_test.go (about)

     1  // Copyright 2019 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package binlog
    15  
    16  import (
    17  	"testing"
    18  
    19  	gmysql "github.com/go-mysql-org/go-mysql/mysql"
    20  	"github.com/pingcap/tiflow/dm/pkg/gtid"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestPositionFromStr(t *testing.T) {
    25  	t.Parallel()
    26  	emptyPos := gmysql.Position{}
    27  	cases := []struct {
    28  		str      string
    29  		pos      gmysql.Position
    30  		hasError bool
    31  	}{
    32  		{
    33  			str:      "mysql-bin.000001",
    34  			pos:      emptyPos,
    35  			hasError: true,
    36  		},
    37  		{
    38  			str:      "234",
    39  			pos:      emptyPos,
    40  			hasError: true,
    41  		},
    42  		{
    43  			str:      "mysql-bin.000001:abc",
    44  			pos:      emptyPos,
    45  			hasError: true,
    46  		},
    47  		{
    48  			str:      "mysql-bin.000001:234:567",
    49  			pos:      emptyPos,
    50  			hasError: true,
    51  		},
    52  		{
    53  			str:      "mysql-bin.000001:234",
    54  			pos:      gmysql.Position{Name: "mysql-bin.000001", Pos: 234},
    55  			hasError: false,
    56  		},
    57  	}
    58  
    59  	for _, cs := range cases {
    60  		pos, err := PositionFromStr(cs.str)
    61  		if cs.hasError {
    62  			require.NotNil(t, err)
    63  		} else {
    64  			require.Nil(t, err)
    65  		}
    66  		require.Equal(t, cs.pos, pos)
    67  	}
    68  }
    69  
    70  func TestRealMySQLPos(t *testing.T) {
    71  	t.Parallel()
    72  	cases := []struct {
    73  		pos       gmysql.Position
    74  		expect    gmysql.Position
    75  		errMsgReg string
    76  	}{
    77  		{
    78  			pos:    gmysql.Position{Name: "mysql-bin.000001", Pos: 154},
    79  			expect: gmysql.Position{Name: "mysql-bin.000001", Pos: 154},
    80  		},
    81  		{
    82  			pos:    gmysql.Position{Name: "mysql-bin|000002.000003", Pos: 154},
    83  			expect: gmysql.Position{Name: "mysql-bin.000003", Pos: 154},
    84  		},
    85  		{
    86  			pos:       gmysql.Position{Name: "", Pos: 154},
    87  			expect:    gmysql.Position{Name: "", Pos: 154},
    88  			errMsgReg: ".*invalid binlog filename.*",
    89  		},
    90  		{
    91  			pos:    gmysql.Position{Name: "mysql|bin|000002.000003", Pos: 154},
    92  			expect: gmysql.Position{Name: "mysql|bin.000003", Pos: 154},
    93  		},
    94  		{
    95  			pos:    gmysql.Position{Name: "mysql-bin|invalid-suffix.000003", Pos: 154},
    96  			expect: gmysql.Position{Name: "mysql-bin|invalid-suffix.000003", Pos: 154},
    97  		},
    98  	}
    99  
   100  	for _, cs := range cases {
   101  		pos, err := RealMySQLPos(cs.pos)
   102  		if len(cs.errMsgReg) > 0 {
   103  			require.Regexp(t, cs.errMsgReg, err)
   104  		} else {
   105  			require.Nil(t, err)
   106  		}
   107  		require.Equal(t, cs.expect, pos)
   108  	}
   109  }
   110  
   111  func TestExtractPos(t *testing.T) {
   112  	t.Parallel()
   113  	cases := []struct {
   114  		pos            gmysql.Position
   115  		uuids          []string
   116  		uuidWithSuffix string
   117  		uuidSuffix     string
   118  		realPos        gmysql.Position
   119  		errMsgReg      string
   120  	}{
   121  		{
   122  			// empty UUIDs
   123  			pos:       gmysql.Position{Name: "mysql-bin.000001", Pos: 666},
   124  			errMsgReg: ".*empty UUIDs.*",
   125  		},
   126  		{
   127  			// invalid UUID in UUIDs
   128  			pos:       gmysql.Position{Name: "mysql-bin.000002", Pos: 666},
   129  			uuids:     []string{"invalid-uuid"},
   130  			errMsgReg: ".*not valid.*",
   131  		},
   132  		{
   133  			// real pos
   134  			pos:            gmysql.Position{Name: "mysql-bin.000003", Pos: 666},
   135  			uuids:          []string{"server-a-uuid.000001", "server-b-uuid.000002", "server-c-uuid.000003"},
   136  			uuidWithSuffix: "server-c-uuid.000003", // use the latest
   137  			uuidSuffix:     "000003",
   138  			realPos:        gmysql.Position{Name: "mysql-bin.000003", Pos: 666},
   139  		},
   140  		{
   141  			// pos match one of UUIDs
   142  			pos:            gmysql.Position{Name: "mysql-bin|000002.000004", Pos: 666},
   143  			uuids:          []string{"server-a-uuid.000001", "server-b-uuid.000002", "server-c-uuid.000003"},
   144  			uuidWithSuffix: "server-b-uuid.000002", // use the latest
   145  			uuidSuffix:     "000002",
   146  			realPos:        gmysql.Position{Name: "mysql-bin.000004", Pos: 666},
   147  		},
   148  		{
   149  			// pos not match one of UUIDs
   150  			pos:       gmysql.Position{Name: "mysql-bin|000111.000005", Pos: 666},
   151  			uuids:     []string{"server-a-uuid.000001", "server-b-uuid.000002", "server-c-uuid.000003"},
   152  			errMsgReg: ".*UUID suffix.*with UUIDs.*",
   153  		},
   154  		{
   155  			// multi `|` exist
   156  			pos:            gmysql.Position{Name: "mysql|bin|000002.000006", Pos: 666},
   157  			uuids:          []string{"server-a-uuid.000001", "server-b-uuid.000002", "server-c-uuid.000003"},
   158  			uuidWithSuffix: "server-b-uuid.000002",
   159  			uuidSuffix:     "000002",
   160  			realPos:        gmysql.Position{Name: "mysql|bin.000006", Pos: 666},
   161  		},
   162  		{
   163  			// invalid UUID suffix
   164  			pos:       gmysql.Position{Name: "mysql-bin|abcdef.000007", Pos: 666},
   165  			uuids:     []string{"server-a-uuid.000001", "server-b-uuid.000002", "server-c-uuid.000003"},
   166  			errMsgReg: ".*invalid UUID suffix.*",
   167  		},
   168  	}
   169  
   170  	for _, cs := range cases {
   171  		uuidWithSuffix, uuidSuffix, realPos, err := ExtractPos(cs.pos, cs.uuids)
   172  		if len(cs.errMsgReg) > 0 {
   173  			require.Regexp(t, cs.errMsgReg, err)
   174  		} else {
   175  			require.Nil(t, err)
   176  		}
   177  		require.Equal(t, cs.uuidWithSuffix, uuidWithSuffix)
   178  		require.Equal(t, cs.uuidSuffix, uuidSuffix)
   179  		require.Equal(t, cs.realPos, realPos)
   180  	}
   181  }
   182  
   183  func TestVerifyUUIDSuffix(t *testing.T) {
   184  	t.Parallel()
   185  	cases := []struct {
   186  		suffix string
   187  		valid  bool
   188  	}{
   189  		{
   190  			suffix: "000666",
   191  			valid:  true,
   192  		},
   193  		{
   194  			suffix: "666888",
   195  			valid:  true,
   196  		},
   197  		{
   198  			// == 0
   199  			suffix: "000000",
   200  		},
   201  		{
   202  			// < 0
   203  			suffix: "-123456",
   204  		},
   205  		{
   206  			// float
   207  			suffix: "123.456",
   208  		},
   209  		{
   210  			// empty
   211  			suffix: "",
   212  		},
   213  		{
   214  			suffix: "abc",
   215  		},
   216  		{
   217  			suffix: "abc666",
   218  		},
   219  		{
   220  			suffix: "666abc",
   221  		},
   222  	}
   223  
   224  	for _, cs := range cases {
   225  		require.Equal(t, cs.valid, verifyRelaySubDirSuffix(cs.suffix))
   226  	}
   227  }
   228  
   229  func TestAdjustPosition(t *testing.T) {
   230  	t.Parallel()
   231  	cases := []struct {
   232  		pos         gmysql.Position
   233  		adjustedPos gmysql.Position
   234  	}{
   235  		{
   236  			gmysql.Position{
   237  				Name: "mysql-bin.00001",
   238  				Pos:  123,
   239  			},
   240  			gmysql.Position{
   241  				Name: "mysql-bin.00001",
   242  				Pos:  123,
   243  			},
   244  		}, {
   245  			gmysql.Position{
   246  				Name: "mysql-bin|00001.00002",
   247  				Pos:  123,
   248  			},
   249  			gmysql.Position{
   250  				Name: "mysql-bin.00002",
   251  				Pos:  123,
   252  			},
   253  		}, {
   254  			gmysql.Position{
   255  				Name: "mysql-bin|00001.00002.00003",
   256  				Pos:  123,
   257  			},
   258  			gmysql.Position{
   259  				Name: "mysql-bin|00001.00002.00003",
   260  				Pos:  123,
   261  			},
   262  		},
   263  	}
   264  
   265  	for _, cs := range cases {
   266  		adjustedPos := RemoveRelaySubDirSuffix(cs.pos)
   267  		require.Equal(t, cs.adjustedPos.Name, adjustedPos.Name)
   268  		require.Equal(t, cs.adjustedPos.Pos, adjustedPos.Pos)
   269  	}
   270  }
   271  
   272  func TestComparePosition(t *testing.T) {
   273  	t.Parallel()
   274  	cases := []struct {
   275  		pos1 gmysql.Position
   276  		pos2 gmysql.Position
   277  		cmp  int
   278  	}{
   279  		{
   280  			gmysql.Position{
   281  				Name: "mysql-bin.00001",
   282  				Pos:  123,
   283  			},
   284  			gmysql.Position{
   285  				Name: "mysql-bin.00002",
   286  				Pos:  123,
   287  			},
   288  			-1,
   289  		}, {
   290  			gmysql.Position{
   291  				Name: "mysql-bin.00001",
   292  				Pos:  123,
   293  			},
   294  			gmysql.Position{
   295  				Name: "mysql-bin.00001",
   296  				Pos:  123,
   297  			},
   298  			0,
   299  		}, {
   300  			gmysql.Position{
   301  				Name: "mysql-bin.00002",
   302  				Pos:  123,
   303  			},
   304  			gmysql.Position{
   305  				Name: "mysql-bin.00001",
   306  				Pos:  123,
   307  			},
   308  			1,
   309  		}, {
   310  			gmysql.Position{
   311  				Name: "mysql-bin|00001.00002",
   312  				Pos:  123,
   313  			},
   314  			gmysql.Position{
   315  				Name: "mysql-bin|00002.00001",
   316  				Pos:  123,
   317  			},
   318  			-1,
   319  		}, {
   320  			gmysql.Position{
   321  				Name: "mysql-bin|00001.00002",
   322  				Pos:  123,
   323  			},
   324  			gmysql.Position{
   325  				Name: "mysql-bin|00001.00002",
   326  				Pos:  123,
   327  			},
   328  			0,
   329  		}, {
   330  			gmysql.Position{
   331  				Name: "mysql-bin|00002.00001",
   332  				Pos:  123,
   333  			},
   334  			gmysql.Position{
   335  				Name: "mysql-bin|00001.00002",
   336  				Pos:  123,
   337  			},
   338  			1,
   339  		},
   340  	}
   341  
   342  	for _, cs := range cases {
   343  		cmp := ComparePosition(cs.pos1, cs.pos2)
   344  		require.Equal(t, cs.cmp, cmp)
   345  	}
   346  }
   347  
   348  func TestCompareCompareLocation(t *testing.T) {
   349  	t.Parallel()
   350  	testCases := []struct {
   351  		flavor  string
   352  		pos1    gmysql.Position
   353  		gset1   string
   354  		suffix1 int
   355  		pos2    gmysql.Position
   356  		gset2   string
   357  		suffix2 int
   358  		cmpGTID int
   359  		cmpPos  int
   360  	}{
   361  		{
   362  			// both gset1 and gset2 is nil, gset1 = gset2, pos1 = pos2
   363  			gmysql.MySQLFlavor,
   364  			gmysql.Position{
   365  				Name: "binlog.00001",
   366  				Pos:  123,
   367  			},
   368  			"",
   369  			0,
   370  			gmysql.Position{
   371  				Name: "binlog.00001",
   372  				Pos:  123,
   373  			},
   374  			"",
   375  			0,
   376  			0,
   377  			0,
   378  		}, {
   379  			// both gset1 and gset2 is nil, gset1 = gset2, pos1 = pos2
   380  			gmysql.MariaDBFlavor,
   381  			gmysql.Position{
   382  				Name: "binlog.00001",
   383  				Pos:  123,
   384  			},
   385  			"",
   386  			0,
   387  			gmysql.Position{
   388  				Name: "binlog.00001",
   389  				Pos:  123,
   390  			},
   391  			"",
   392  			0,
   393  			0,
   394  			0,
   395  		}, {
   396  			// both gset1 and gset2 is nil, gset1 = gset2, pos1 < pos2
   397  			gmysql.MariaDBFlavor,
   398  			gmysql.Position{
   399  				Name: "binlog.00001",
   400  				Pos:  123,
   401  			},
   402  			"",
   403  			0,
   404  			gmysql.Position{
   405  				Name: "binlog.00002",
   406  				Pos:  122,
   407  			},
   408  			"",
   409  			0,
   410  			0,
   411  			-1,
   412  		}, {
   413  			// pos1 > pos2, gset is nil
   414  			gmysql.MySQLFlavor,
   415  			gmysql.Position{
   416  				Name: "binlog.00003",
   417  				Pos:  123,
   418  			},
   419  			"",
   420  			0,
   421  			gmysql.Position{
   422  				Name: "binlog.00002",
   423  				Pos:  122,
   424  			},
   425  			"",
   426  			0,
   427  			0,
   428  			1,
   429  		}, {
   430  			// gset1 = gset2, pos1 < pos2
   431  			gmysql.MySQLFlavor,
   432  			gmysql.Position{
   433  				Name: "binlog.00001",
   434  				Pos:  123,
   435  			},
   436  			"53ea0ed1-9bf8-11e6-8bea-64006a897c73:1-4",
   437  			0,
   438  			gmysql.Position{
   439  				Name: "binlog.00002",
   440  				Pos:  122,
   441  			},
   442  			"53ea0ed1-9bf8-11e6-8bea-64006a897c73:1-4",
   443  			0,
   444  			0,
   445  			-1,
   446  		}, {
   447  			// gset1 < gset2, pos1 < pos2
   448  			gmysql.MySQLFlavor,
   449  			gmysql.Position{
   450  				Name: "binlog.00001",
   451  				Pos:  123,
   452  			},
   453  			"53ea0ed1-9bf8-11e6-8bea-64006a897c73:1-2,53ea0ed1-9bf8-11e6-8bea-64006a897c74:1-2",
   454  			0,
   455  			gmysql.Position{
   456  				Name: "binlog.00002",
   457  				Pos:  124,
   458  			},
   459  			"53ea0ed1-9bf8-11e6-8bea-64006a897c73:1-4,53ea0ed1-9bf8-11e6-8bea-64006a897c74:1-3",
   460  			0,
   461  			-1,
   462  			-1,
   463  		}, {
   464  			// gset1 > gset2, pos1 < pos1
   465  			gmysql.MySQLFlavor,
   466  			gmysql.Position{
   467  				Name: "binlog.00001",
   468  				Pos:  123,
   469  			},
   470  			"53ea0ed1-9bf8-11e6-8bea-64006a897c73:1-2,53ea0ed1-9bf8-11e6-8bea-64006a897c74:1-3",
   471  			0,
   472  			gmysql.Position{
   473  				Name: "binlog.00002",
   474  				Pos:  124,
   475  			},
   476  			"53ea0ed1-9bf8-11e6-8bea-64006a897c73:1-2",
   477  			0,
   478  			1,
   479  			-1,
   480  		}, {
   481  			// can't compare by gtid set, pos1 < pos2
   482  			gmysql.MySQLFlavor,
   483  			gmysql.Position{
   484  				Name: "binlog.00001",
   485  				Pos:  123,
   486  			},
   487  			"53ea0ed1-9bf8-11e6-8bea-64006a897c73:1-2,53ea0ed1-9bf8-11e6-8bea-64006a897c74:2-4",
   488  			0,
   489  			gmysql.Position{
   490  				Name: "binlog.00002",
   491  				Pos:  124,
   492  			},
   493  			"53ea0ed1-9bf8-11e6-8bea-64006a897c73:1-2,53ea0ed1-9bf8-11e6-8bea-64006a897c74:1-3",
   494  			0,
   495  			-1,
   496  			-1,
   497  		}, {
   498  			// gset1 = gset2, pos1 < pos2
   499  			gmysql.MariaDBFlavor,
   500  			gmysql.Position{
   501  				Name: "binlog.00001",
   502  				Pos:  123,
   503  			},
   504  			"1-1-1,2-2-2",
   505  			0,
   506  			gmysql.Position{
   507  				Name: "binlog.00002",
   508  				Pos:  122,
   509  			},
   510  			"1-1-1,2-2-2",
   511  			0,
   512  			0,
   513  			-1,
   514  		}, {
   515  			// gset1 < gset2, pos1 < pos2
   516  			gmysql.MariaDBFlavor,
   517  			gmysql.Position{
   518  				Name: "binlog.00001",
   519  				Pos:  123,
   520  			},
   521  			"1-1-1,2-2-2",
   522  			0,
   523  			gmysql.Position{
   524  				Name: "binlog.00002",
   525  				Pos:  124,
   526  			},
   527  			"1-1-1,2-2-2,3-3-3",
   528  			0,
   529  			-1,
   530  			-1,
   531  		}, {
   532  			// gset1 > gset2, pos1 < pos2
   533  			gmysql.MariaDBFlavor,
   534  			gmysql.Position{
   535  				Name: "binlog.00001",
   536  				Pos:  123,
   537  			},
   538  			"1-1-1,2-2-3",
   539  			0,
   540  			gmysql.Position{
   541  				Name: "binlog.00002",
   542  				Pos:  124,
   543  			},
   544  			"1-1-1,2-2-2",
   545  			0,
   546  			1,
   547  			-1,
   548  		}, {
   549  			// can't compare by gtid set, pos1 < pos2
   550  			gmysql.MariaDBFlavor,
   551  			gmysql.Position{
   552  				Name: "binlog.00001",
   553  				Pos:  123,
   554  			},
   555  			"1-1-1,2-2-2",
   556  			0,
   557  			gmysql.Position{
   558  				Name: "binlog.00002",
   559  				Pos:  124,
   560  			},
   561  			"2-2-2,3-3-3",
   562  			0,
   563  			-1,
   564  			-1,
   565  		}, {
   566  			// gset1 is nil < gset2, pos1 < pos2
   567  			gmysql.MariaDBFlavor,
   568  			gmysql.Position{
   569  				Name: "binlog.00001",
   570  				Pos:  123,
   571  			},
   572  			"",
   573  			0,
   574  			gmysql.Position{
   575  				Name: "binlog.00002",
   576  				Pos:  124,
   577  			},
   578  			"2-2-2,3-3-3",
   579  			0,
   580  			-1,
   581  			-1,
   582  		}, {
   583  			// both gset1 and gset2 is nil, gset1 = gset2, pos1 < pos2
   584  			gmysql.MariaDBFlavor,
   585  			gmysql.Position{
   586  				Name: "binlog.00001",
   587  				Pos:  123,
   588  			},
   589  			"",
   590  			0,
   591  			gmysql.Position{
   592  				Name: "binlog.00002",
   593  				Pos:  124,
   594  			},
   595  			"",
   596  			0,
   597  			0,
   598  			-1,
   599  		}, {
   600  			// both gset1 and gset2 is nil, gset1 = gset2, pos1 < pos2
   601  			gmysql.MariaDBFlavor,
   602  			gmysql.Position{
   603  				Name: "binlog.00001",
   604  				Pos:  123,
   605  			},
   606  			"",
   607  			0,
   608  			gmysql.Position{
   609  				Name: "binlog.00002",
   610  				Pos:  124,
   611  			},
   612  			"",
   613  			0,
   614  			0,
   615  			-1,
   616  		}, {
   617  			// gset1 = gset2, pos1 = pos2, suffix1 < suffix2
   618  			gmysql.MariaDBFlavor,
   619  			gmysql.Position{
   620  				Name: "binlog.00001",
   621  				Pos:  123,
   622  			},
   623  			"1-1-1,2-2-2",
   624  			0,
   625  			gmysql.Position{
   626  				Name: "binlog.00001",
   627  				Pos:  123,
   628  			},
   629  			"1-1-1,2-2-2",
   630  			1,
   631  			-1,
   632  			-1,
   633  		}, {
   634  			// gset1 = gset2, pos1 = pos2, suffix1 = suffix2
   635  			gmysql.MariaDBFlavor,
   636  			gmysql.Position{
   637  				Name: "binlog.00001",
   638  				Pos:  123,
   639  			},
   640  			"1-1-1,2-2-2",
   641  			1,
   642  			gmysql.Position{
   643  				Name: "binlog.00001",
   644  				Pos:  123,
   645  			},
   646  			"1-1-1,2-2-2",
   647  			1,
   648  			0,
   649  			0,
   650  		}, {
   651  			// gset1 = gset2, pos1 = pos2, suffix1 > suffix2
   652  			gmysql.MariaDBFlavor,
   653  			gmysql.Position{
   654  				Name: "binlog.00001",
   655  				Pos:  123,
   656  			},
   657  			"1-1-1,2-2-2",
   658  			2,
   659  			gmysql.Position{
   660  				Name: "binlog.00001",
   661  				Pos:  123,
   662  			},
   663  			"1-1-1,2-2-2",
   664  			1,
   665  			1,
   666  			1,
   667  		},
   668  	}
   669  
   670  	for _, cs := range testCases {
   671  		t.Log(cs)
   672  		gset1, err := gtid.ParserGTID(cs.flavor, cs.gset1)
   673  		require.Nil(t, err)
   674  		gset2, err := gtid.ParserGTID(cs.flavor, cs.gset2)
   675  		require.Nil(t, err)
   676  
   677  		cmpGTID := CompareLocation(Location{cs.pos1, gset1, cs.suffix1}, Location{cs.pos2, gset2, cs.suffix2}, true)
   678  		require.Equal(t, cs.cmpGTID, cmpGTID)
   679  
   680  		cmpPos := CompareLocation(Location{cs.pos1, gset1, cs.suffix1}, Location{cs.pos2, gset2, cs.suffix2}, false)
   681  		require.Equal(t, cs.cmpPos, cmpPos)
   682  	}
   683  }
   684  
   685  func TestVerifyBinlogPos(t *testing.T) {
   686  	t.Parallel()
   687  	cases := []struct {
   688  		input  string
   689  		hasErr bool
   690  		pos    *gmysql.Position
   691  	}{
   692  		{
   693  			`"mysql-bin.000001:2345"`,
   694  			false,
   695  			&gmysql.Position{Name: "mysql-bin.000001", Pos: 2345},
   696  		},
   697  		{
   698  			`mysql-bin.000001:2345`,
   699  			false,
   700  			&gmysql.Position{Name: "mysql-bin.000001", Pos: 2345},
   701  		},
   702  		{
   703  			`"mysql-bin.000001"`,
   704  			true,
   705  			nil,
   706  		},
   707  		{
   708  			`mysql-bin.000001`,
   709  			true,
   710  			nil,
   711  		},
   712  	}
   713  
   714  	for _, ca := range cases {
   715  		ret, err := VerifyBinlogPos(ca.input)
   716  		if ca.hasErr {
   717  			require.NotNil(t, err)
   718  		} else {
   719  			require.Equal(t, ca.pos, ret)
   720  		}
   721  	}
   722  }
   723  
   724  func TestSetGTID(t *testing.T) {
   725  	t.Parallel()
   726  	GTIDSetStr := "3ccc475b-2343-11e7-be21-6c0b84d59f30:1-14"
   727  	GTIDSetStr2 := "3ccc475b-2343-11e7-be21-6c0b84d59f30:1-15"
   728  	gset, _ := gtid.ParserGTID("mysql", GTIDSetStr)
   729  	gset2, _ := gtid.ParserGTID("mysql", GTIDSetStr2)
   730  	mysqlSet := gset
   731  	mysqlSet2 := gset2
   732  
   733  	loc := Location{
   734  		Position: gmysql.Position{
   735  			Name: "mysql-bin.00002",
   736  			Pos:  2333,
   737  		},
   738  		gtidSet: gset,
   739  		Suffix:  0,
   740  	}
   741  	loc2 := loc
   742  
   743  	require.Equal(t, 0, CompareLocation(loc, loc2, false))
   744  	loc2.Position.Pos++
   745  	require.Equal(t, uint32(2333), loc.Position.Pos)
   746  	require.Equal(t, -1, CompareLocation(loc, loc2, false))
   747  
   748  	loc2.Position.Name = "mysql-bin.00001"
   749  	require.Equal(t, "mysql-bin.00002", loc.Position.Name)
   750  	require.Equal(t, 1, CompareLocation(loc, loc2, false))
   751  
   752  	// will not change other location's gtid
   753  	loc2.gtidSet = mysqlSet2
   754  	require.NotEqual(t, GTIDSetStr2, loc.gtidSet.String())
   755  	require.Equal(t, GTIDSetStr2, loc2.gtidSet.String())
   756  
   757  	err := loc2.SetGTID(mysqlSet2)
   758  	require.Nil(t, err)
   759  	require.Equal(t, GTIDSetStr, loc.gtidSet.String())
   760  	require.Equal(t, GTIDSetStr2, loc2.gtidSet.String())
   761  	require.Equal(t, -1, CompareLocation(loc, loc2, true))
   762  
   763  	loc2.gtidSet = nil
   764  	err = loc2.SetGTID(mysqlSet)
   765  	require.Nil(t, err)
   766  	require.Equal(t, GTIDSetStr, loc2.gtidSet.String())
   767  }
   768  
   769  func TestSetGTIDMariaDB(t *testing.T) {
   770  	t.Parallel()
   771  	gSetStr := "1-1-1,2-2-2"
   772  	gSet, err := gtid.ParserGTID("mariadb", gSetStr)
   773  	require.Nil(t, err)
   774  	gSetOrigin := gSet
   775  
   776  	loc := Location{
   777  		Position: gmysql.Position{
   778  			Name: "mysql-bin.00002",
   779  			Pos:  2333,
   780  		},
   781  		gtidSet: nil,
   782  		Suffix:  0,
   783  	}
   784  	err = loc.SetGTID(gSetOrigin)
   785  	require.Nil(t, err)
   786  	require.Equal(t, gSetStr, loc.gtidSet.String())
   787  }
   788  
   789  func TestExtractSuffix(t *testing.T) {
   790  	t.Parallel()
   791  	testCases := []struct {
   792  		name   string
   793  		suffix int
   794  	}{
   795  		{
   796  			"",
   797  			MinRelaySubDirSuffix,
   798  		},
   799  		{
   800  			"mysql-bin.00005",
   801  			MinRelaySubDirSuffix,
   802  		},
   803  		{
   804  			"mysql-bin|000001.000001",
   805  			1,
   806  		},
   807  		{
   808  			"mysql-bin|000005.000004",
   809  			5,
   810  		},
   811  	}
   812  
   813  	for _, tc := range testCases {
   814  		suffix, err := ExtractSuffix(tc.name)
   815  		require.Nil(t, err)
   816  		require.Equal(t, tc.suffix, suffix)
   817  	}
   818  }
   819  
   820  func TestIsFreshPosition(t *testing.T) {
   821  	t.Parallel()
   822  	mysqlPos := gmysql.Position{
   823  		Name: "mysql-binlog.00001",
   824  		Pos:  123,
   825  	}
   826  	mysqlGTIDSet, err := gtid.ParserGTID(gmysql.MySQLFlavor, "e8e592a6-7a59-11eb-8da1-0242ac110002:1-36")
   827  	require.Nil(t, err)
   828  	mariaGTIDSet, err := gtid.ParserGTID(gmysql.MariaDBFlavor, "0-1001-233")
   829  	require.Nil(t, err)
   830  	testCases := []struct {
   831  		loc     Location
   832  		flavor  string
   833  		cmpGTID bool
   834  		fresh   bool
   835  	}{
   836  		{
   837  			NewLocation(mysqlPos, mysqlGTIDSet),
   838  			gmysql.MySQLFlavor,
   839  			true,
   840  			false,
   841  		},
   842  		{
   843  			NewLocation(mysqlPos, gtid.MustZeroGTIDSet(gmysql.MySQLFlavor)),
   844  			gmysql.MySQLFlavor,
   845  			true,
   846  			false,
   847  		},
   848  		{
   849  			NewLocation(MinPosition, mysqlGTIDSet),
   850  			gmysql.MySQLFlavor,
   851  			true,
   852  			false,
   853  		},
   854  		{
   855  			NewLocation(MinPosition, mysqlGTIDSet),
   856  			gmysql.MySQLFlavor,
   857  			false,
   858  			true,
   859  		},
   860  		{
   861  			NewLocation(MinPosition, gtid.MustZeroGTIDSet(gmysql.MySQLFlavor)),
   862  			gmysql.MySQLFlavor,
   863  			true,
   864  			true,
   865  		},
   866  
   867  		{
   868  			NewLocation(mysqlPos, mariaGTIDSet),
   869  			gmysql.MariaDBFlavor,
   870  			true,
   871  			false,
   872  		},
   873  		{
   874  			NewLocation(mysqlPos, gtid.MustZeroGTIDSet(gmysql.MariaDBFlavor)),
   875  			gmysql.MariaDBFlavor,
   876  			true,
   877  			false,
   878  		},
   879  		{
   880  			NewLocation(MinPosition, mariaGTIDSet),
   881  			gmysql.MariaDBFlavor,
   882  			true,
   883  			false,
   884  		},
   885  		{
   886  			NewLocation(MinPosition, mariaGTIDSet),
   887  			gmysql.MariaDBFlavor,
   888  			false,
   889  			true,
   890  		},
   891  		{
   892  			NewLocation(MinPosition, gtid.MustZeroGTIDSet(gmysql.MariaDBFlavor)),
   893  			gmysql.MariaDBFlavor,
   894  			true,
   895  			true,
   896  		},
   897  	}
   898  
   899  	for _, tc := range testCases {
   900  		fresh := IsFreshPosition(tc.loc, tc.flavor, tc.cmpGTID)
   901  		require.Equal(t, tc.fresh, fresh)
   902  	}
   903  }