go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/client/butler/callback_text_wrapper_test.go (about)

     1  // Copyright 2018 The LUCI Authors.
     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 butler
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	"go.chromium.org/luci/logdog/api/logpb"
    22  
    23  	. "github.com/smartystreets/goconvey/convey"
    24  	. "go.chromium.org/luci/common/testing/assertions"
    25  )
    26  
    27  type line struct {
    28  	value string
    29  	delim string
    30  }
    31  
    32  func mkTextLogEntry(lines []line, seq uint64) *logpb.LogEntry {
    33  	logLines := make([]*logpb.Text_Line, 0, len(lines))
    34  	for _, l := range lines {
    35  		logLines = append(logLines, &logpb.Text_Line{
    36  			Value:     []byte(l.value),
    37  			Delimiter: l.delim,
    38  		})
    39  	}
    40  	le := &logpb.LogEntry{
    41  		Sequence: seq,
    42  		Content: &logpb.LogEntry_Text{
    43  			Text: &logpb.Text{Lines: logLines},
    44  		},
    45  	}
    46  	return le
    47  }
    48  
    49  func mkWrappedTextCb(values *[]string, seq *[]uint64) StreamChunkCallback {
    50  	cb := func(le *logpb.LogEntry) {
    51  		if le == nil {
    52  			return
    53  		}
    54  		for _, l := range le.GetText().Lines {
    55  			*values = append(*values, fmt.Sprintf(
    56  				"%s!%s", string(l.Value), l.Delimiter,
    57  			))
    58  		}
    59  		*seq = append(*seq, le.Sequence)
    60  	}
    61  	return getWrappedTextCallback(cb)
    62  }
    63  
    64  func TestTextReassembler(t *testing.T) {
    65  	t.Parallel()
    66  
    67  	Convey(`Callback wrapper works`, t, func() {
    68  		Convey(`With nil`, func() {
    69  			values, seq := []string{}, []uint64{}
    70  			mkWrappedTextCb(&values, &seq)(nil)
    71  			So(values, ShouldResemble, []string{})
    72  		})
    73  
    74  		Convey(`With only complete lines`, func() {
    75  			values, seq := []string{}, []uint64{}
    76  			mkWrappedTextCb(&values, &seq)(mkTextLogEntry([]line{
    77  				{"hi", "\n"},
    78  				{"there", "\n"},
    79  			}, 0))
    80  			So(values, ShouldResemble, []string{
    81  				"hi!\n",
    82  				"there!\n",
    83  			})
    84  			So(seq, ShouldResemble, []uint64{0})
    85  		})
    86  
    87  		Convey(`With partial lines`, func() {
    88  			values, seq := []string{}, []uint64{}
    89  			cbWrapped := mkWrappedTextCb(&values, &seq)
    90  
    91  			Convey(`At the beginning of a LogEntry`, func() {
    92  				cbWrapped(mkTextLogEntry([]line{
    93  					{"h", ""},
    94  				}, 0))
    95  				So(values, ShouldResemble, []string{})
    96  				So(seq, ShouldResemble, []uint64{})
    97  			})
    98  
    99  			Convey(`At the end of a LogEntry`, func() {
   100  				cbWrapped(mkTextLogEntry([]line{
   101  					{"hi", "\n"},
   102  					{"there", "\n"},
   103  					{"ho", ""},
   104  				}, 0))
   105  				So(values, ShouldResemble, []string{
   106  					"hi!\n",
   107  					"there!\n",
   108  				})
   109  				So(seq, ShouldResemble, []uint64{0})
   110  
   111  				Convey(`And correctly completes with the next LogEntry`, func() {
   112  					cbWrapped(mkTextLogEntry([]line{
   113  						{"w are you", "\n"},
   114  					}, 2))
   115  					So(values, ShouldResemble, []string{
   116  						"hi!\n",
   117  						"there!\n",
   118  						"how are you!\n",
   119  					})
   120  					So(seq, ShouldResemble, []uint64{0, 2})
   121  				})
   122  
   123  				Convey(`Flushes when called with niil`, func() {
   124  					cbWrapped(nil)
   125  					So(values, ShouldResemble, []string{
   126  						"hi!\n",
   127  						"there!\n",
   128  						"ho!",
   129  					})
   130  					So(seq, ShouldResemble, []uint64{0, 2})
   131  				})
   132  			})
   133  		})
   134  	})
   135  
   136  	Convey(`Callback wrapper panics`, t, func() {
   137  		cbWrapped := mkWrappedTextCb(nil, nil)
   138  
   139  		Convey(`When called on non-text LogEntries`, func() {
   140  			So(
   141  				func() {
   142  					cbWrapped(&logpb.LogEntry{Content: &logpb.LogEntry_Datagram{}})
   143  				},
   144  				ShouldPanicLike,
   145  				"expected *logpb.LogEntry_Text",
   146  			)
   147  		})
   148  
   149  		Convey(`When the partial line is not in expected location`, func() {
   150  			Convey(`Like the first of multiple lines`, func() {
   151  				So(
   152  					func() {
   153  						cbWrapped(mkTextLogEntry([]line{
   154  							{"w ar", ""},
   155  							{"e yo", ""},
   156  						}, 2))
   157  					},
   158  					ShouldPanicLike,
   159  					"partial line not last",
   160  				)
   161  			})
   162  
   163  			Convey(`Like interspersed with complete lines`, func() {
   164  				So(
   165  					func() {
   166  						cbWrapped(mkTextLogEntry([]line{
   167  							{"w", "\n"},
   168  							{"ar", ""},
   169  							{"e you", "\n"},
   170  						}, 2))
   171  					},
   172  					ShouldPanicLike,
   173  					"partial line not last",
   174  				)
   175  			})
   176  		})
   177  	})
   178  }