go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/luciexe/host/buildmerge/build_test.go (about)

     1  // Copyright 2019 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 buildmerge
    16  
    17  import (
    18  	"errors"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/golang/protobuf/ptypes"
    23  	"google.golang.org/protobuf/proto"
    24  	"google.golang.org/protobuf/types/known/structpb"
    25  
    26  	bbpb "go.chromium.org/luci/buildbucket/proto"
    27  	"go.chromium.org/luci/common/clock/testclock"
    28  
    29  	. "github.com/smartystreets/goconvey/convey"
    30  
    31  	. "go.chromium.org/luci/common/testing/assertions"
    32  )
    33  
    34  func TestSetErrorOnBuild(t *testing.T) {
    35  	t.Parallel()
    36  	Convey(`setErrorOnBuild`, t, func() {
    37  		Convey(`basic`, func() {
    38  			build := &bbpb.Build{
    39  				Output: &bbpb.Build_Output{},
    40  			}
    41  			setErrorOnBuild(build, errors.New("hi"))
    42  			So(build, ShouldResembleProto, &bbpb.Build{
    43  				SummaryMarkdown: "\n\nError in build protocol: hi",
    44  				Status:          bbpb.Status_INFRA_FAILURE,
    45  				Output: &bbpb.Build_Output{
    46  					Status:          bbpb.Status_INFRA_FAILURE,
    47  					SummaryMarkdown: "\n\nError in build protocol: hi",
    48  				},
    49  			})
    50  		})
    51  
    52  		Convey(`truncated message`, func() {
    53  			build := &bbpb.Build{
    54  				SummaryMarkdown: strings.Repeat("16 test pattern\n", 256),
    55  				Output:          &bbpb.Build_Output{},
    56  			}
    57  			setErrorOnBuild(build, errors.New("hi"))
    58  			So(build.SummaryMarkdown, ShouldHaveLength, 4096)
    59  			So(build.SummaryMarkdown, ShouldEndWith, "...\n\nError in build protocol: hi")
    60  			So(build.Output.SummaryMarkdown, ShouldEqual, build.SummaryMarkdown)
    61  			build.SummaryMarkdown = ""
    62  			build.Output.SummaryMarkdown = ""
    63  			So(build, ShouldResembleProto, &bbpb.Build{
    64  				Status: bbpb.Status_INFRA_FAILURE,
    65  				Output: &bbpb.Build_Output{
    66  					Status: bbpb.Status_INFRA_FAILURE,
    67  				},
    68  			})
    69  		})
    70  	})
    71  }
    72  
    73  func TestProcessFinalBuild(t *testing.T) {
    74  	t.Parallel()
    75  	Convey(`processFinalBuild`, t, func() {
    76  		now, err := ptypes.TimestampProto(testclock.TestRecentTimeLocal)
    77  		So(err, ShouldBeNil)
    78  
    79  		Convey(`empty`, func() {
    80  			build := &bbpb.Build{
    81  				Output: &bbpb.Build_Output{},
    82  			}
    83  			processFinalBuild(now, build)
    84  			So(build, ShouldResembleProto, &bbpb.Build{
    85  				SummaryMarkdown: ("\n\nError in build protocol: " +
    86  					"Expected a terminal build status, got STATUS_UNSPECIFIED, while top level status is STATUS_UNSPECIFIED."),
    87  				UpdateTime: now,
    88  				EndTime:    now,
    89  				Status:     bbpb.Status_INFRA_FAILURE,
    90  				Output: &bbpb.Build_Output{
    91  					Status: bbpb.Status_INFRA_FAILURE,
    92  					SummaryMarkdown: ("\n\nError in build protocol: " +
    93  						"Expected a terminal build status, got STATUS_UNSPECIFIED, while top level status is STATUS_UNSPECIFIED."),
    94  				},
    95  			})
    96  		})
    97  
    98  		Convey(`success`, func() {
    99  			build := &bbpb.Build{
   100  				Status: bbpb.Status_SUCCESS,
   101  				Steps: []*bbpb.Step{
   102  					{Status: bbpb.Status_SUCCESS},
   103  				},
   104  				Output: &bbpb.Build_Output{
   105  					Status: bbpb.Status_SUCCESS,
   106  				},
   107  			}
   108  			processFinalBuild(now, build)
   109  			So(build, ShouldResembleProto, &bbpb.Build{
   110  				UpdateTime: now,
   111  				EndTime:    now,
   112  				Status:     bbpb.Status_SUCCESS,
   113  				Steps: []*bbpb.Step{
   114  					{Status: bbpb.Status_SUCCESS, EndTime: now},
   115  				},
   116  				Output: &bbpb.Build_Output{
   117  					Status: bbpb.Status_SUCCESS,
   118  				},
   119  			})
   120  		})
   121  
   122  		Convey(`incomplete step`, func() {
   123  			build := &bbpb.Build{
   124  				Status: bbpb.Status_SUCCESS,
   125  				Steps: []*bbpb.Step{
   126  					{Status: bbpb.Status_SUCCESS},
   127  					{SummaryMarkdown: "hi"},
   128  				},
   129  				Output: &bbpb.Build_Output{
   130  					Status: bbpb.Status_SUCCESS,
   131  				},
   132  			}
   133  			processFinalBuild(now, build)
   134  			So(build, ShouldResembleProto, &bbpb.Build{
   135  				UpdateTime: now,
   136  				EndTime:    now,
   137  				Status:     bbpb.Status_SUCCESS,
   138  				Steps: []*bbpb.Step{
   139  					{Status: bbpb.Status_SUCCESS, EndTime: now},
   140  					{
   141  						Status:          bbpb.Status_CANCELED,
   142  						EndTime:         now,
   143  						SummaryMarkdown: "hi\nstep was never finalized; did the build crash?",
   144  					},
   145  				},
   146  				Output: &bbpb.Build_Output{
   147  					Status: bbpb.Status_SUCCESS,
   148  				},
   149  			})
   150  		})
   151  	})
   152  }
   153  
   154  func TestUpdateStepFromBuild(t *testing.T) {
   155  	Convey(`updateStepFromBuild`, t, func() {
   156  		now, err := ptypes.TimestampProto(testclock.TestRecentTimeLocal)
   157  		So(err, ShouldBeNil)
   158  
   159  		Convey(`basic`, func() {
   160  			step := &bbpb.Step{
   161  				Logs: []*bbpb.Log{{Name: "something"}},
   162  			}
   163  			build := &bbpb.Build{
   164  				SummaryMarkdown: "hi",
   165  				Status:          bbpb.Status_FAILURE,
   166  				EndTime:         now,
   167  				Output: &bbpb.Build_Output{
   168  					Logs:   []*bbpb.Log{{Name: "other"}},
   169  					Status: bbpb.Status_FAILURE,
   170  				},
   171  			}
   172  			updateStepFromBuild(step, build)
   173  			So(step, ShouldResembleProto, &bbpb.Step{
   174  				SummaryMarkdown: "hi",
   175  				Status:          bbpb.Status_FAILURE,
   176  				EndTime:         now,
   177  				Logs: []*bbpb.Log{
   178  					{Name: "something"},
   179  					{Name: "other"},
   180  				},
   181  			})
   182  		})
   183  		Convey(`step status terminal`, func() {
   184  			step := &bbpb.Step{
   185  				Status:          bbpb.Status_INFRA_FAILURE,
   186  				SummaryMarkdown: "hi step",
   187  				EndTime:         now,
   188  				Logs:            []*bbpb.Log{{Name: "step something"}},
   189  			}
   190  			build := &bbpb.Build{
   191  				SummaryMarkdown: "hi sub build",
   192  				Status:          bbpb.Status_FAILURE,
   193  				Output: &bbpb.Build_Output{
   194  					Logs:   []*bbpb.Log{{Name: "build other"}},
   195  					Status: bbpb.Status_FAILURE,
   196  				},
   197  			}
   198  			updateStepFromBuild(step, build)
   199  			So(step, ShouldResembleProto, &bbpb.Step{
   200  				SummaryMarkdown: "hi step",
   201  				Status:          bbpb.Status_INFRA_FAILURE,
   202  				EndTime:         now,
   203  				Logs: []*bbpb.Log{
   204  					{Name: "step something"},
   205  				},
   206  			})
   207  		})
   208  	})
   209  }
   210  
   211  func TestUpdateBaseFromUserbuild(t *testing.T) {
   212  	Convey(`updateBaseFromUserBuild`, t, func() {
   213  		now, err := ptypes.TimestampProto(testclock.TestRecentTimeLocal)
   214  		So(err, ShouldBeNil)
   215  
   216  		Convey(`basic`, func() {
   217  			base := &bbpb.Build{
   218  				Steps: []*bbpb.Step{{Name: "sup"}},
   219  			}
   220  			build := &bbpb.Build{
   221  				SummaryMarkdown: "hi",
   222  				Status:          bbpb.Status_CANCELED,
   223  				StatusDetails: &bbpb.StatusDetails{
   224  					Timeout: &bbpb.StatusDetails_Timeout{},
   225  				},
   226  				UpdateTime: now,
   227  				EndTime:    now,
   228  				Tags: []*bbpb.StringPair{
   229  					{Key: "hi", Value: "there"},
   230  				},
   231  				Output: &bbpb.Build_Output{
   232  					Logs:   []*bbpb.Log{{Name: "other"}},
   233  					Status: bbpb.Status_CANCELED,
   234  					StatusDetails: &bbpb.StatusDetails{
   235  						Timeout: &bbpb.StatusDetails_Timeout{},
   236  					},
   237  				},
   238  			}
   239  			buildClone := proto.Clone(build).(*bbpb.Build)
   240  			buildClone.Steps = append(buildClone.Steps, &bbpb.Step{Name: "sup"})
   241  			updateBaseFromUserBuild(base, build)
   242  			So(base, ShouldResembleProto, buildClone)
   243  		})
   244  
   245  		Convey(`nil build`, func() {
   246  			base := &bbpb.Build{
   247  				Steps:           []*bbpb.Step{{Name: "sup"}},
   248  				SummaryMarkdown: "hi",
   249  			}
   250  			baseClone := proto.Clone(base).(*bbpb.Build)
   251  			updateBaseFromUserBuild(base, nil)
   252  			So(base, ShouldResembleProto, baseClone)
   253  		})
   254  
   255  		Convey(`output is merged`, func() {
   256  			base := &bbpb.Build{
   257  				Output: &bbpb.Build_Output{
   258  					Logs: []*bbpb.Log{
   259  						{Name: "hello"},
   260  					},
   261  				},
   262  			}
   263  			updateBaseFromUserBuild(base, &bbpb.Build{
   264  				Output: &bbpb.Build_Output{
   265  					Logs: []*bbpb.Log{
   266  						{Name: "world"},
   267  					},
   268  				},
   269  			})
   270  			So(base, ShouldResembleProto, &bbpb.Build{
   271  				Output: &bbpb.Build_Output{
   272  					Logs: []*bbpb.Log{
   273  						{Name: "hello"},
   274  						{Name: "world"},
   275  					},
   276  				},
   277  			})
   278  		})
   279  	})
   280  }
   281  
   282  func TestUpdateBuildFromGlobalSubBuild(t *testing.T) {
   283  	Convey(`TestUpdateBuildFromGlobalSubBuild`, t, func() {
   284  		base := &bbpb.Build{}
   285  		sub := &bbpb.Build{}
   286  
   287  		Convey(`empty parent`, func() {
   288  			Convey(`empty child`, func() {
   289  				updateBuildFromGlobalSubBuild(base, sub)
   290  				So(base, ShouldResembleProto, &bbpb.Build{})
   291  			})
   292  
   293  			Convey(`properties`, func() {
   294  				s, err := structpb.NewStruct(map[string]any{
   295  					"hello": "world",
   296  					"this":  100,
   297  				})
   298  				So(err, ShouldBeNil)
   299  				sub.Output = &bbpb.Build_Output{Properties: s}
   300  				updateBuildFromGlobalSubBuild(base, sub)
   301  				m := base.Output.Properties.AsMap()
   302  				So(m, ShouldResemble, map[string]any{
   303  					"hello": "world",
   304  					"this":  100.0, // because JSON semantics
   305  				})
   306  			})
   307  		})
   308  
   309  		Convey(`populated parent`, func() {
   310  			s, err := structpb.NewStruct(map[string]any{
   311  				"hello": "world",
   312  				"this":  100,
   313  			})
   314  			So(err, ShouldBeNil)
   315  			base.Output = &bbpb.Build_Output{
   316  				Properties: s,
   317  			}
   318  
   319  			Convey(`empty child`, func() {
   320  				updateBuildFromGlobalSubBuild(base, sub)
   321  				So(base, ShouldResembleProto, &bbpb.Build{
   322  					Output: &bbpb.Build_Output{
   323  						Properties: s,
   324  					},
   325  				})
   326  			})
   327  
   328  			Convey(`properties`, func() {
   329  				sSub, err := structpb.NewStruct(map[string]any{
   330  					"newkey": "yes",
   331  					"hello":  "replacement",
   332  				})
   333  				So(err, ShouldBeNil)
   334  				sub.Output = &bbpb.Build_Output{Properties: sSub}
   335  				updateBuildFromGlobalSubBuild(base, sub)
   336  
   337  				sNew, err := structpb.NewStruct(map[string]any{
   338  					"hello":  "replacement",
   339  					"this":   100,
   340  					"newkey": "yes",
   341  				})
   342  				So(err, ShouldBeNil)
   343  				So(base, ShouldResembleProto, &bbpb.Build{
   344  					Output: &bbpb.Build_Output{
   345  						Properties: sNew,
   346  					},
   347  				})
   348  			})
   349  
   350  		})
   351  	})
   352  }