package hcl import ( "bytes" "fmt" "reflect" "testing" ) func TestRangeOver(t *testing.T) { tests := []struct { A Range B Range Want Range }{ { Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // #### Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ##### Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ##### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, }, { Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // ### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // ### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // ## Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 2, Line: 1, Column: 3}, }, Range{ // ## Start: Pos{Byte: 4, Line: 1, Column: 5}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, Range{ // ###### Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, }, { Range{ // ## Start: Pos{Byte: 4, Line: 1, Column: 5}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, Range{ // ## Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 2, Line: 1, Column: 3}, }, Range{ // ###### Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, }, } for _, test := range tests { t.Run(fmt.Sprintf("%s<=>%s", test.A, test.B), func(t *testing.T) { got := RangeOver(test.A, test.B) if !reflect.DeepEqual(got, test.Want) { t.Errorf( "wrong result\nA : %-10s %s\nB : %-10s %s\ngot : %-10s %s\nwant: %-10s %s", visRangeOffsets(test.A), test.A, visRangeOffsets(test.B), test.B, visRangeOffsets(got), got, visRangeOffsets(test.Want), test.Want, ) } }) } } func TestPosOverlap(t *testing.T) { tests := []struct { A Range B Range Want Range }{ { Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, }, { Range{ // #### Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, }, { Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, }, { Range{ // ### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, }, { Range{ // ### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // ## Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 2, Line: 1, Column: 3}, }, Range{ // ## Start: Pos{Byte: 4, Line: 1, Column: 5}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, Range{ // (no overlap) Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 0, Line: 1, Column: 1}, }, }, { Range{ // ## Start: Pos{Byte: 4, Line: 1, Column: 5}, End: Pos{Byte: 6, Line: 1, Column: 7}, }, Range{ // ## Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 2, Line: 1, Column: 3}, }, Range{ // (no overlap) Start: Pos{Byte: 4, Line: 1, Column: 5}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, }, } for _, test := range tests { t.Run(fmt.Sprintf("%s<=>%s", test.A, test.B), func(t *testing.T) { got := test.A.Overlap(test.B) if !reflect.DeepEqual(got, test.Want) { t.Errorf( "wrong result\nA : %-10s %s\nB : %-10s %s\ngot : %-10s %s\nwant: %-10s %s", visRangeOffsets(test.A), test.A, visRangeOffsets(test.B), test.B, visRangeOffsets(got), got, visRangeOffsets(test.Want), test.Want, ) } }) } } func TestRangePartitionAround(t *testing.T) { tests := []struct { Outer Range Inner Range WantBefore Range WantOverlap Range WantAfter Range }{ { Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // (empty) Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 2, Line: 1, Column: 3}, }, Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // (empty) Start: Pos{Byte: 4, Line: 1, Column: 5}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, }, { Range{ // #### Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // # Start: Pos{Byte: 0, Line: 1, Column: 1}, End: Pos{Byte: 1, Line: 1, Column: 2}, }, Range{ // ### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // (empty) Start: Pos{Byte: 4, Line: 1, Column: 5}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, }, { Range{ // #### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // (empty) Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 2, Line: 1, Column: 3}, }, Range{ // ### Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // # Start: Pos{Byte: 5, Line: 1, Column: 6}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, { Range{ // #### Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // # Start: Pos{Byte: 1, Line: 1, Column: 2}, End: Pos{Byte: 2, Line: 1, Column: 3}, }, Range{ // ## Start: Pos{Byte: 2, Line: 1, Column: 3}, End: Pos{Byte: 4, Line: 1, Column: 5}, }, Range{ // # Start: Pos{Byte: 4, Line: 1, Column: 5}, End: Pos{Byte: 5, Line: 1, Column: 6}, }, }, } for _, test := range tests { t.Run(fmt.Sprintf("%s around %s", test.Outer, test.Inner), func(t *testing.T) { gotBefore, gotOverlap, gotAfter := test.Outer.PartitionAround(test.Inner) if !reflect.DeepEqual(gotBefore, test.WantBefore) { t.Errorf( "wrong before\nA : %-10s %s\nB : %-10s %s\ngot : %-10s %s\nwant: %-10s %s", visRangeOffsets(test.Outer), test.Outer, visRangeOffsets(test.Inner), test.Inner, visRangeOffsets(gotBefore), gotBefore, visRangeOffsets(test.WantBefore), test.WantBefore, ) } if !reflect.DeepEqual(gotOverlap, test.WantOverlap) { t.Errorf( "wrong overlap\nA : %-10s %s\nB : %-10s %s\ngot : %-10s %s\nwant: %-10s %s", visRangeOffsets(test.Outer), test.Outer, visRangeOffsets(test.Inner), test.Inner, visRangeOffsets(gotOverlap), gotOverlap, visRangeOffsets(test.WantOverlap), test.WantOverlap, ) } if !reflect.DeepEqual(gotAfter, test.WantAfter) { t.Errorf( "wrong after\nA : %-10s %s\nB : %-10s %s\ngot : %-10s %s\nwant: %-10s %s", visRangeOffsets(test.Outer), test.Outer, visRangeOffsets(test.Inner), test.Inner, visRangeOffsets(gotAfter), gotAfter, visRangeOffsets(test.WantAfter), test.WantAfter, ) } }) } } // visRangeOffsets is a helper that produces a visual representation of the // start and end byte offsets of the given range, which can then be stacked // with the same for other ranges to more easily see how the ranges relate // to one another. func visRangeOffsets(rng Range) string { var buf bytes.Buffer if rng.End.Byte < rng.Start.Byte { // Should never happen, but we'll visualize it anyway so we can // more easily debug failing tests. for i := 0; i < rng.End.Byte; i++ { buf.WriteByte(' ') } for i := rng.End.Byte; i < rng.Start.Byte; i++ { buf.WriteByte('!') } return buf.String() } for i := 0; i < rng.Start.Byte; i++ { buf.WriteByte(' ') } for i := rng.Start.Byte; i < rng.End.Byte; i++ { buf.WriteByte('#') } return buf.String() }