go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/compilefailureanalysis/heuristic/signal_extractor_test.go (about)

     1  // Copyright 2022 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 heuristic
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	. "github.com/smartystreets/goconvey/convey"
    22  	"go.chromium.org/luci/bisection/model"
    23  )
    24  
    25  func TestExtractSignal(t *testing.T) {
    26  	t.Parallel()
    27  	c := context.Background()
    28  	Convey("Extract Ninja Log", t, func() {
    29  		Convey("No Log Should throw error", func() {
    30  			failureLog := &model.CompileLogs{}
    31  			_, e := ExtractSignals(c, failureLog)
    32  			So(e, ShouldNotBeNil)
    33  		})
    34  		Convey("No output", func() {
    35  			failureLog := &model.CompileLogs{
    36  				NinjaLog: &model.NinjaLog{
    37  					Failures: []*model.NinjaLogFailure{
    38  						{
    39  							Rule:         "CXX",
    40  							OutputNodes:  []string{"n1", "n2"},
    41  							Dependencies: []string{"a/b/../c.d", "../../a.b"},
    42  						},
    43  						{
    44  							Rule:         "CC",
    45  							OutputNodes:  []string{"n3", "n4"},
    46  							Dependencies: []string{"x/y/./z", "d\\e\\f"},
    47  						},
    48  					},
    49  				},
    50  			}
    51  			signal, e := ExtractSignals(c, failureLog)
    52  			So(e, ShouldBeNil)
    53  			So(signal, ShouldResemble, &model.CompileFailureSignal{
    54  				Nodes: []string{"n1", "n2", "n3", "n4"},
    55  				Edges: []*model.CompileFailureEdge{
    56  					{
    57  						Rule:         "CXX",
    58  						OutputNodes:  []string{"n1", "n2"},
    59  						Dependencies: []string{"a/c.d", "a.b"},
    60  					},
    61  					{
    62  						Rule:         "CC",
    63  						OutputNodes:  []string{"n3", "n4"},
    64  						Dependencies: []string{"x/y/z", "d/e/f"},
    65  					},
    66  				},
    67  			})
    68  			Convey("Python patterns", func() {
    69  				failureLog := &model.CompileLogs{
    70  					NinjaLog: &model.NinjaLog{
    71  						Failures: []*model.NinjaLogFailure{
    72  							{
    73  								Rule:         "CXX",
    74  								OutputNodes:  []string{"n1", "n2"},
    75  								Dependencies: []string{"a/b/../c.d", "../../a.b"},
    76  								Output: `
    77  method1 at path/a.py:1
    78  message1
    79  method2 at ../path/b.py:2
    80  message2
    81  method3 at path/a.py:3
    82  message3
    83  blablaError: blabla...
    84  
    85  blabla
    86  
    87  File "path/a.py", line 14, in method1
    88  message1
    89  File "path/c.py", line 34, in method2
    90  message2
    91  blabalError: blabla...`,
    92  							},
    93  						},
    94  					},
    95  				}
    96  				signal, e := ExtractSignals(c, failureLog)
    97  				So(e, ShouldBeNil)
    98  				So(signal, ShouldResemble, &model.CompileFailureSignal{
    99  					Nodes: []string{"n1", "n2"},
   100  					Edges: []*model.CompileFailureEdge{
   101  						{
   102  							Rule:         "CXX",
   103  							OutputNodes:  []string{"n1", "n2"},
   104  							Dependencies: []string{"a/c.d", "a.b"},
   105  						},
   106  					},
   107  					Files: map[string][]int{
   108  						"path/a.py": {1, 3, 14},
   109  						"path/b.py": {2},
   110  						"path/c.py": {34},
   111  					},
   112  				})
   113  			})
   114  
   115  			Convey("NonPython patterns", func() {
   116  				failureLog := &model.CompileLogs{
   117  					NinjaLog: &model.NinjaLog{
   118  						Failures: []*model.NinjaLogFailure{
   119  							{
   120  								Rule:         "CXX",
   121  								OutputNodes:  []string{"obj/a/b/test.c.o"},
   122  								Dependencies: []string{"../../a/b/c.c", "../../a.b"},
   123  								Output: `/b/build/goma/gomacc blabla ... -c ../../a/b/c.c -o obj/a/b/test.c.o
   124  ../../a/b/c.c:307:44: error:no member 'kEnableExtensionInfoDialog' ...
   125  Error
   126  ../../d.cpp error:Undeclared variable ...
   127  Error
   128  blah blah c:\\a\\b.txt:12 error
   129  Error c:\a\b.txt(123) blah blah
   130  D:\\x\\y.cc[line 456]
   131  //BUILD.gn
   132  1 error generated.`,
   133  							},
   134  						},
   135  					},
   136  				}
   137  				signal, e := ExtractSignals(c, failureLog)
   138  				So(e, ShouldBeNil)
   139  				So(signal, ShouldResemble, &model.CompileFailureSignal{
   140  					Nodes: []string{"obj/a/b/test.c.o"},
   141  					Edges: []*model.CompileFailureEdge{
   142  						{
   143  							Rule:         "CXX",
   144  							OutputNodes:  []string{"obj/a/b/test.c.o"},
   145  							Dependencies: []string{"a/b/c.c", "a.b"},
   146  						},
   147  					},
   148  					Files: map[string][]int{
   149  						"a/b/c.c":    {307},
   150  						"d.cpp":      {},
   151  						"c:/a/b.txt": {12, 123},
   152  						"D:/x/y.cc":  {456},
   153  						"BUILD.gn":   {},
   154  					},
   155  				})
   156  			})
   157  		})
   158  	})
   159  
   160  	Convey("Extract Stdout Log", t, func() {
   161  		Convey("Stdout", func() {
   162  			failureLog := &model.CompileLogs{
   163  				NinjaLog: nil,
   164  				StdOutLog: `
   165  [1832/2467 | 117.498] CXX obj/a/b/test.file.o
   166  blabla...
   167  FAILED: obj/a/b/test.c.o
   168  /b/build/goma/gomacc blabla ... -c ../../a/b/c.cc -o obj/a/b/test.c.o
   169  ../../a/b/c.cc:307:44: error: no member 'kEnableExtensionInfoDialog' ...
   170  1 error generated.
   171  x/y/not_in_signal.cc
   172  FAILED: obj/a/b/test.d.o
   173  /b/build/goma/gomacc blabla ... -c ../../a/b/d.cc -o obj/a/b/test.d.o
   174  ../../a/b/d.cc:123:44: error: no member 'kEnableExtensionInfoDialog' ...
   175  blabla...
   176  1 error generated.
   177  FAILED: obj/a/b/test.e.o "another node 1" obj/a/b/test.f.o "another node 2" "another node 3"
   178  /b/build/goma/gomacc ... ../../a/b/e.cc ... obj/a/b/test.e.o
   179  ../../a/b/e.cc:79:44: error: no member 'kEnableExtensionInfoDialog' ...
   180  blabla...
   181  ninja: build stopped: subcommand failed.
   182  
   183  /b/build/goma/goma_ctl.sh stat
   184  blabla...
   185  				`,
   186  			}
   187  			signal, e := ExtractSignals(c, failureLog)
   188  			So(e, ShouldBeNil)
   189  			So(signal, ShouldResemble, &model.CompileFailureSignal{
   190  				Files: map[string][]int{
   191  					"a/b/c.cc":         {307},
   192  					"a/b/d.cc":         {123},
   193  					"a/b/e.cc":         {79},
   194  					"obj/a/b/test.c.o": {},
   195  					"obj/a/b/test.d.o": {},
   196  					"obj/a/b/test.e.o": {},
   197  				},
   198  				Nodes: []string{
   199  					"obj/a/b/test.c.o",
   200  					"obj/a/b/test.d.o",
   201  					"obj/a/b/test.e.o",
   202  					"another node 1",
   203  					"obj/a/b/test.f.o",
   204  					"another node 2",
   205  					"another node 3",
   206  				},
   207  			})
   208  		})
   209  	})
   210  }