...

Source file src/github.com/golang/geo/s2/textformat_test_test.go

Documentation: github.com/golang/geo/s2

     1  // Copyright 2017 Google Inc. All rights reserved.
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package s2
    16  
    17  import (
    18  	"bytes"
    19  	"testing"
    20  
    21  	"github.com/golang/geo/r1"
    22  	"github.com/golang/geo/r3"
    23  	"github.com/golang/geo/s1"
    24  )
    25  
    26  func TestParseLatLng(t *testing.T) {
    27  	tests := []struct {
    28  		have string
    29  		want []LatLng
    30  	}{
    31  		{
    32  			have: "",
    33  			want: []LatLng{},
    34  		},
    35  		{
    36  			have: "blah",
    37  			want: []LatLng{},
    38  		},
    39  		{
    40  			have: "0:0",
    41  			want: []LatLng{LatLng{0, 0}},
    42  		},
    43  		{
    44  			have: "0:0, 0:-90",
    45  			want: []LatLng{
    46  				LatLngFromDegrees(0, 0),
    47  				LatLngFromDegrees(0, -90),
    48  			},
    49  		},
    50  		{
    51  			have: "-20:150, -20:151, -19:150",
    52  			want: []LatLng{
    53  				LatLngFromDegrees(-20, 150),
    54  				LatLngFromDegrees(-20, 151),
    55  				LatLngFromDegrees(-19, 150),
    56  			},
    57  		},
    58  	}
    59  
    60  	for _, test := range tests {
    61  		got := parseLatLngs(test.have)
    62  
    63  		if len(got) != len(test.want) {
    64  			t.Errorf("parseLatLngs(%s) = %+v, got different number of results %+v", test.have, got, test.want)
    65  			continue
    66  		}
    67  
    68  		for k, v := range got {
    69  			if v != test.want[k] {
    70  				t.Errorf("parseLatlng(%q): %d. %+v, want %+v", test.have, k, v, test.want[k])
    71  			}
    72  		}
    73  	}
    74  }
    75  
    76  func TestTextFormatWritePoints(t *testing.T) {
    77  	tests := []struct {
    78  		have []Point
    79  		want string
    80  	}{
    81  		{
    82  			have: nil,
    83  			want: "",
    84  		},
    85  		{
    86  			have: []Point{},
    87  			want: "",
    88  		},
    89  		{
    90  			have: []Point{PointFromCoords(1, 0, 0)},
    91  			want: "0:0",
    92  		},
    93  		{
    94  			have: []Point{PointFromCoords(1, 0, 0), PointFromCoords(0, -1, 0)},
    95  			want: "0:0, 0:-90",
    96  		},
    97  	}
    98  
    99  	for _, test := range tests {
   100  		var buf bytes.Buffer
   101  		writePoints(&buf, test.have)
   102  		if got := buf.String(); got != test.want {
   103  			t.Errorf("writePoints(%v) = %q, want %q", test.have, got, test.want)
   104  		}
   105  	}
   106  }
   107  
   108  func TestTextFormatParsePointRoundtrip(t *testing.T) {
   109  	tests := []struct {
   110  		have string
   111  		want Point
   112  	}{
   113  		{"0:0", Point{r3.Vector{1, 0, 0}}},
   114  		{"90:0", Point{r3.Vector{6.123233995736757e-17, 0, 1}}},
   115  		{"-45:0", Point{r3.Vector{0.7071067811865476, 0, -0.7071067811865475}}},
   116  		{"0:0.01", Point{r3.Vector{0.9999999847691292, 0.00017453292431333684, 0}}},
   117  		{"0:30", Point{r3.Vector{0.8660254037844387, 0.49999999999999994, 0}}},
   118  		{"0:45", Point{r3.Vector{0.7071067811865476, 0.7071067811865475, 0}}},
   119  		{"0:90", Point{r3.Vector{6.123233995736757e-17, 1, 0}}},
   120  		{"30:30", Point{r3.Vector{0.7500000000000001, 0.4330127018922193, 0.49999999999999994}}},
   121  		{"-30:30", Point{r3.Vector{0.7500000000000001, 0.4330127018922193, -0.49999999999999994}}},
   122  		{"0:180", Point{r3.Vector{-1, 6.123233995736757e-17, 0}}},
   123  		{"0:-180", Point{r3.Vector{-1, -6.123233995736757e-17, 0}}},
   124  		{"90:-180", Point{r3.Vector{-6.123233995736757e-17, -0, 1}}},
   125  		{"1e-20:1e-30", Point{r3.Vector{1, 0, 0}}},
   126  	}
   127  
   128  	for _, test := range tests {
   129  		pt := parsePoint(test.have)
   130  		if !pt.ApproxEqual(test.want) {
   131  			t.Errorf("parsePoint(%s) = %v, want %v", test.have, pt, test.want)
   132  		}
   133  		if got := pointToString(pt); got != test.have {
   134  			t.Errorf("pointToString(parsePoint(%v)) = %v, want %v", test.have, got, test.have)
   135  		}
   136  	}
   137  }
   138  
   139  func TestTextFormatParsePointRoundtripEdgecases(t *testing.T) {
   140  	tests := []struct {
   141  		have    string
   142  		wantPt  Point
   143  		wantStr string
   144  	}{
   145  		// just past pole, lng should shift by 180
   146  		{
   147  			have:    "91:0",
   148  			wantPt:  Point{r3.Vector{-0.017452406437283473, 0, 0.9998476951563913}},
   149  			wantStr: "89:-180",
   150  		},
   151  		{
   152  			have:    "-91:0",
   153  			wantPt:  Point{r3.Vector{-0.017452406437283473, -0, -0.9998476951563913}},
   154  			wantStr: "-89:-180",
   155  		},
   156  
   157  		// The conversion from degrees to radians and back leads to little
   158  		// bits of floating point noise, so we end up with things like
   159  		// 7.01e-15 instead of 0.
   160  
   161  		// values wrap around past the North pole back past the equator on the
   162  		// other side of the earth.
   163  		{
   164  			have:    "179.99:0",
   165  			wantPt:  Point{r3.Vector{-0.9999999847691292, -0, 0.00017453292431344843}},
   166  			wantStr: "0.0100000000000064:-180",
   167  		},
   168  		/*
   169  			// TODO(roberts): This test output differs between gccgo and 6g in the last significant digit.
   170  			{
   171  				have:    "180:0",
   172  				wantPt:  Point{r3.Vector{-1, -0, 1.2246467991473515e-16}},
   173  				wantStr: "7.01670929853487e-15:-180",
   174  			},
   175  		*/
   176  		{
   177  			have:    "181.0:0",
   178  			wantPt:  Point{r3.Vector{-0.9998476951563913, -0, -0.017452406437283637}},
   179  			wantStr: "-1.00000000000001:-180",
   180  		},
   181  		/*
   182  			// past pole to equator, lng should shift by 180 as well.
   183  			// TODO(roberts): This test output differs between gccgo and 6g in the last significant digit.
   184  			{
   185  				have:    "-180:90",
   186  				wantPt:  Point{r3.Vector{-6.123233995736757e-17, -1, 1.2246467991473515e-16}},
   187  				wantStr: "-7.01670929853487e-15:-90",
   188  			},
   189  		*/
   190  
   191  		// string contains more than one value, only first is used in making a point.
   192  		{
   193  			have:    "37.4210:-122.0866, 37.4231:-122.0819",
   194  			wantPt:  Point{r3.Vector{-0.4218751185559026, -0.6728760966593905, 0.6076669670863027}},
   195  			wantStr: "37.421:-122.0866",
   196  		},
   197  	}
   198  
   199  	for _, test := range tests {
   200  		pt := parsePoint(test.have)
   201  		if !pt.ApproxEqual(test.wantPt) {
   202  			t.Errorf("parsePoint(%s) = %v, want %v", test.have, pt, test.wantPt)
   203  		}
   204  		if got := pointToString(pt); got != test.wantStr {
   205  			t.Errorf("pointToString(parsePoint(%v)) = %v, want %v", test.have, got, test.wantStr)
   206  		}
   207  	}
   208  }
   209  
   210  func TestTextFormatParsePointsLatLngs(t *testing.T) {
   211  	tests := []struct {
   212  		have    string
   213  		wantPts []Point
   214  		wantLLs []LatLng
   215  	}{
   216  		{
   217  			have:    "0:0",
   218  			wantPts: []Point{{r3.Vector{1, 0, 0}}},
   219  			wantLLs: []LatLng{{Lat: 0, Lng: 0}},
   220  		},
   221  		{
   222  			have:    "      0:0,    ",
   223  			wantPts: []Point{{r3.Vector{1, 0, 0}}},
   224  			wantLLs: []LatLng{{Lat: 0, Lng: 0}},
   225  		},
   226  		{
   227  			have: "90:0,-90:0",
   228  			wantPts: []Point{
   229  				{r3.Vector{6.123233995736757e-17, 0, 1}},
   230  				{r3.Vector{6.123233995736757e-17, 0, -1}},
   231  			},
   232  			wantLLs: []LatLng{
   233  				{Lat: 90 * s1.Degree, Lng: 0},
   234  				{Lat: -90 * s1.Degree, Lng: 0},
   235  			},
   236  		},
   237  		{
   238  			have: "90:0, 0:90, -90:0, 0:-90",
   239  			wantPts: []Point{
   240  				{r3.Vector{6.123233995736757e-17, 0, 1}},
   241  				{r3.Vector{6.123233995736757e-17, 1, 0}},
   242  				{r3.Vector{6.123233995736757e-17, 0, -1}},
   243  				{r3.Vector{6.123233995736757e-17, -1, 0}},
   244  			},
   245  			wantLLs: []LatLng{
   246  				{Lat: 90 * s1.Degree, Lng: 0},
   247  				{Lat: 0, Lng: 90 * s1.Degree},
   248  				{Lat: -90 * s1.Degree, Lng: 0},
   249  				{Lat: 0, Lng: -90 * s1.Degree},
   250  			},
   251  		},
   252  		{
   253  			have: "37.4210:-122.0866, 37.4231:-122.0819",
   254  			wantPts: []Point{
   255  				{r3.Vector{-0.421875118555903, -0.672876096659391, 0.607666967086303}},
   256  				{r3.Vector{-0.421808091075447, -0.672891829588934, 0.607696075333505}},
   257  			},
   258  			wantLLs: []LatLng{
   259  				{s1.Degree * 37.4210, s1.Degree * -122.0866},
   260  				{s1.Degree * 37.4231, s1.Degree * -122.0819},
   261  			},
   262  		},
   263  		{
   264  			// empty string, should get back nothing.
   265  			have: "",
   266  		},
   267  		{
   268  			// two empty elements, both should be skipped.
   269  			have: ",",
   270  		},
   271  		{
   272  			// Oversized values should come through as expected.
   273  			have: "9000:1234.56",
   274  			wantPts: []Point{
   275  				{r3.Vector{-0.903035619536086, 0.429565675827430, 9.82193362e-16}},
   276  			},
   277  
   278  			wantLLs: []LatLng{
   279  				{Lat: 9000 * s1.Degree, Lng: 1234.56 * s1.Degree},
   280  			},
   281  		},
   282  	}
   283  
   284  	for _, test := range tests {
   285  		for i, pt := range parsePoints(test.have) {
   286  			if !pt.ApproxEqual(test.wantPts[i]) {
   287  				t.Errorf("parsePoints(%s): [%d]: got %v, want %v", test.have, i, pt, test.wantPts[i])
   288  			}
   289  		}
   290  
   291  		// TODO(roberts): Test the roundtrip back to pointsToString()
   292  
   293  		for i, ll := range parseLatLngs(test.have) {
   294  			if ll != test.wantLLs[i] {
   295  				t.Errorf("parseLatLngs(%s): [%d]: got %v, want %v", test.have, i, ll, test.wantLLs[i])
   296  			}
   297  		}
   298  
   299  		// TODO(roberts): Test the roundtrip back to latlngssToString()
   300  	}
   301  }
   302  
   303  func TestTextFormatParseRect(t *testing.T) {
   304  	tests := []struct {
   305  		have string
   306  		want Rect
   307  	}{
   308  		{"0:0", Rect{}},
   309  		{
   310  			"1:1",
   311  			Rect{
   312  				r1.Interval{float64(s1.Degree), float64(s1.Degree)},
   313  				s1.Interval{float64(s1.Degree), float64(s1.Degree)},
   314  			},
   315  		},
   316  		{
   317  			"1:1, 2:2, 3:3",
   318  			Rect{
   319  				r1.Interval{float64(s1.Degree), 3 * float64(s1.Degree)},
   320  				s1.Interval{float64(s1.Degree), 3 * float64(s1.Degree)},
   321  			},
   322  		},
   323  		{
   324  			"-90:-180, 90:180",
   325  			Rect{
   326  				r1.Interval{-90 * float64(s1.Degree), 90 * float64(s1.Degree)},
   327  				s1.Interval{180 * float64(s1.Degree), -180 * float64(s1.Degree)},
   328  			},
   329  		},
   330  		{
   331  			"-89.99:0, 89.99:179.99",
   332  			Rect{
   333  				r1.Interval{-89.99 * float64(s1.Degree), 89.99 * float64(s1.Degree)},
   334  				s1.Interval{0, 179.99 * float64(s1.Degree)},
   335  			},
   336  		},
   337  		{
   338  			"-89.99:-179.99, 89.99:179.99",
   339  			Rect{
   340  				r1.Interval{-89.99 * float64(s1.Degree), 89.99 * float64(s1.Degree)},
   341  				s1.Interval{179.99 * float64(s1.Degree), -179.99 * float64(s1.Degree)},
   342  			},
   343  		},
   344  		{
   345  			"37.4210:-122.0866, 37.4231:-122.0819",
   346  			Rect{
   347  				r1.Interval{float64(s1.Degree * 37.4210), float64(s1.Degree * 37.4231)},
   348  				s1.Interval{float64(s1.Degree * -122.0866), float64(s1.Degree * -122.0819)},
   349  			},
   350  		},
   351  		{
   352  			"-876.54:-654.43, 963.84:2468.35",
   353  			Rect{
   354  				r1.Interval{-876.54 * float64(s1.Degree), -876.54 * float64(s1.Degree)},
   355  				s1.Interval{-654.43 * float64(s1.Degree), -654.43 * float64(s1.Degree)},
   356  			},
   357  		},
   358  	}
   359  	for _, test := range tests {
   360  		if got := makeRect(test.have); got != test.want {
   361  			t.Errorf("makeRect(%s) = %v, want %v", test.have, got, test.want)
   362  		}
   363  	}
   364  }
   365  
   366  func TestTextFormatMakeCellUnion(t *testing.T) {
   367  	tests := []struct {
   368  		have []string
   369  		want CellUnion
   370  	}{
   371  		{
   372  			have: nil,
   373  			want: CellUnion{},
   374  		},
   375  		{
   376  			have: []string{},
   377  			want: CellUnion{},
   378  		},
   379  		{
   380  			have: []string{"google"},
   381  			want: CellUnion{0},
   382  		},
   383  		{
   384  			have: []string{"0/"},
   385  			want: CellUnion{CellIDFromFace(0)},
   386  		},
   387  		{
   388  			have: []string{"2/010", "2/011", "2/02"},
   389  			want: CellUnion{0x4240000000000000, 0x42c0000000000000, 0x4500000000000000},
   390  		},
   391  	}
   392  
   393  	for _, test := range tests {
   394  		got := makeCellUnion(test.have...)
   395  		got.Normalize()
   396  		if !got.Equal(test.want) {
   397  			t.Errorf("makeCellUnion(%v) = %v, want %v", test.have, got, test.want)
   398  		}
   399  	}
   400  }
   401  
   402  func TestTextFormatMakeLaxPolyline(t *testing.T) {
   403  	l := makeLaxPolyline("-20:150, -20:151, -19:150")
   404  
   405  	// No easy equality check for laxPolylines; check vertices instead.
   406  	if len(l.vertices) != 3 {
   407  		t.Errorf("len(l.vertices) = %d, want 3", len(l.vertices))
   408  	}
   409  	if got, want := LatLngFromPoint(l.vertices[0]), LatLngFromDegrees(-20, 150); !latLngsApproxEqual(got, want, epsilon) {
   410  		t.Errorf("vertex(0) = %v, want %v", got, want)
   411  	}
   412  	if got, want := LatLngFromPoint(l.vertices[1]), LatLngFromDegrees(-20, 151); !latLngsApproxEqual(got, want, epsilon) {
   413  		t.Errorf("vertex(1) = %v, want %v", got, want)
   414  	}
   415  	if got, want := LatLngFromPoint(l.vertices[2]), LatLngFromDegrees(-19, 150); !latLngsApproxEqual(got, want, epsilon) {
   416  		t.Errorf("vertex(2) = %v, want %v", got, want)
   417  	}
   418  
   419  	// TODO(roberts): test out an invalid value
   420  	// makeLaxPolyline("blah")
   421  }
   422  
   423  func TestTextFormatMakeLaxPolygonEmpty(t *testing.T) {
   424  	// Verify that "" and "empty" both create empty polygons.
   425  	shape := makeLaxPolygon("")
   426  	if got, want := shape.numLoops, 0; got != want {
   427  		t.Errorf("laxPolygon.numLoops = %d, want %d", got, want)
   428  	}
   429  	shape = makeLaxPolygon("empty")
   430  	if got, want := shape.numLoops, 0; got != want {
   431  		t.Errorf("laxPolygon.numLoops = %d, want %d", got, want)
   432  	}
   433  }
   434  
   435  func TestTextFormatMakeLaxPolygonFull(t *testing.T) {
   436  	shape := makeLaxPolygon("full")
   437  	if got, want := shape.numLoops, 1; got != want {
   438  		t.Errorf("laxPolygon.numLoops = %d, want %d", got, want)
   439  	}
   440  	if got, want := shape.numLoopVertices(0), 0; got != want {
   441  		t.Errorf("laxPolygon.numLoopVertices(%d) = %d, want %d", 0, got, want)
   442  	}
   443  }
   444  
   445  func TestTextFormatMakeLaxPolygonFullWithHole(t *testing.T) {
   446  	shape := makeLaxPolygon("full; 0:0")
   447  	if got, want := shape.numLoops, 2; got != want {
   448  		t.Errorf("laxPolygon.numLoops = %d, want %d", got, want)
   449  	}
   450  	if got, want := shape.numLoopVertices(0), 0; got != want {
   451  		t.Errorf("laxPolygon.numLoopVertices(%d) = %d, want %d", 0, got, want)
   452  	}
   453  	if got, want := shape.numLoopVertices(1), 1; got != want {
   454  		t.Errorf("laxPolygon.numLoopVertices(%d) = %d, want %d", 1, got, want)
   455  
   456  	}
   457  	if got, want := shape.NumEdges(), 1; got != want {
   458  		t.Errorf("laxPolygon.NumEdges() = %d, want %d", got, want)
   459  	}
   460  }
   461  
   462  func TestTextFormatShapeIndexDebugStringRoundTrip(t *testing.T) {
   463  	tests := []string{
   464  		"# #",
   465  		"0:0 # #",
   466  		"0:0 | 1:0 # #",
   467  		"0:0 | 1:0 # #",
   468  		"# 0:0, 0:0 #",
   469  		"# 0:0, 0:0 | 1:0, 2:0 #",
   470  		"# # 0:0",
   471  		"# # 0:0, 0:1",
   472  		"# # 0:0, 0:1, 1:0",
   473  		"# # 0:0, 0:1, 1:0, 2:2",
   474  	}
   475  
   476  	for _, want := range tests {
   477  		if got := shapeIndexDebugString(makeShapeIndex(want)); got != want {
   478  			t.Errorf("ShapeIndex failed roundtrip to string. got %q, want %q", got, want)
   479  		}
   480  	}
   481  }
   482  
   483  // TODO(roberts): Remaining tests
   484  // to debug string tests for
   485  //   SpecialCases, EmptyLoop, EmptyPolyline, Empty Othertypes
   486  //
   487  // make type tests for ValidInput and InvalidInput for
   488  //   Points, Rect, Loop, Polyline, Polygon,
   489  //   LaxPolygon, ShapeIndex
   490  

View as plain text