1// Copyright 2022 Google LLC 2// 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5 6package exporter 7 8import ( 9 "bytes" 10 "path/filepath" 11 "testing" 12 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/mock" 15 "github.com/stretchr/testify/require" 16 "go.skia.org/infra/go/skerr" 17 "go.skia.org/skia/bazel/exporter/build_proto/analysis_v2" 18 "go.skia.org/skia/bazel/exporter/interfaces/mocks" 19 "google.golang.org/protobuf/encoding/prototext" 20) 21 22const testWorkspaceDir = "/path/to/workspace" 23const testCMakeOutFname = "/not/necessarily/in/workspace/CMakeLists.txt" 24 25// This input test data (in textproto format) started as output of 26// a bazel cquery call - like: 27// 28// bazel cquery --noimplicit_deps 'kind("rule", deps(//:core))' --output textproto 29// 30// and then hand edited to create a small valid query result with specific 31// files, copts, and other cc_library/cc_binary rule attributes. 32const textProto = `results { 33 target { 34 type: RULE 35 rule { 36 name: "//src/libs:sum" 37 rule_class: "cc_library" 38 location: "/path/to/workspace/src/libs/BUILD.bazel:8:11" 39 attribute { 40 name: "copts" 41 type: STRING_LIST 42 string_list_value: "-O2" 43 explicitly_specified: true 44 nodep: false 45 } 46 attribute { 47 name: "defines" 48 type: STRING_LIST 49 string_list_value: "SUMDEF" 50 explicitly_specified: false 51 nodep: false 52 } 53 attribute { 54 name: "includes" 55 type: STRING_LIST 56 string_list_value: "." 57 explicitly_specified: false 58 nodep: false 59 } 60 attribute { 61 name: "linkopts" 62 type: STRING_LIST 63 string_list_value: "-L/library/dir" 64 explicitly_specified: false 65 nodep: false 66 } 67 attribute { 68 name: "name" 69 type: STRING 70 string_value: "sum" 71 explicitly_specified: true 72 nodep: false 73 } 74 attribute { 75 name: "srcs" 76 type: LABEL_LIST 77 string_list_value: "//src/libs:sum.cpp" 78 explicitly_specified: true 79 nodep: false 80 } 81 attribute { 82 name: "hdrs" 83 type: LABEL_LIST 84 string_list_value: "//src/libs:sum.h" 85 explicitly_specified: true 86 nodep: false 87 } 88 attribute { 89 name: "visibility" 90 type: STRING_LIST 91 string_list_value: "//visibility:public" 92 explicitly_specified: true 93 nodep: true 94 } 95 } 96 }, 97 } 98 results { 99 target { 100 type: RULE 101 rule { 102 name: "//src/apps:hello" 103 rule_class: "cc_binary" 104 location: "/path/to/workspace/src/apps/BUILD.bazel:8:11" 105 attribute { 106 name: "copts" 107 type: STRING_LIST 108 string_list_value: "-O1" 109 explicitly_specified: true 110 nodep: false 111 } 112 attribute { 113 name: "defines" 114 type: STRING_LIST 115 string_list_value: "APPDEF" 116 explicitly_specified: false 117 nodep: false 118 } 119 attribute { 120 name: "linkopts" 121 type: STRING_LIST 122 string_list_value: "-L/app/dir" 123 explicitly_specified: false 124 nodep: false 125 } 126 attribute { 127 name: "name" 128 type: STRING 129 string_value: "hello" 130 explicitly_specified: true 131 nodep: false 132 } 133 attribute { 134 name: "srcs" 135 type: LABEL_LIST 136 string_list_value: "//src/apps:hello-world.cpp" 137 explicitly_specified: true 138 nodep: false 139 } 140 attribute { 141 name: "deps" 142 type: LABEL_LIST 143 string_list_value: "//src/libs:sum" 144 explicitly_specified: false 145 nodep: false 146 } 147 attribute { 148 name: "visibility" 149 type: STRING_LIST 150 string_list_value: "//visibility:public" 151 explicitly_specified: true 152 nodep: true 153 } 154 } 155 } 156 }` 157 158func TestExport_QueryReadError_ReturnsError(t *testing.T) { 159 fs := mocks.NewFileSystem(t) 160 e := NewCMakeExporter("projName", testWorkspaceDir, testCMakeOutFname, fs) 161 qcmd := mocks.NewQueryCommand(t) 162 qcmd.On("Read", mock.Anything).Return([]byte{}, skerr.Fmt("expected error")) 163 err := e.Export(qcmd) 164 assert.Error(t, err) 165} 166 167func TestExport_InvalidProtobuf_ReturnsError(t *testing.T) { 168 fs := mocks.NewFileSystem(t) 169 e := NewCMakeExporter("projName", testWorkspaceDir, testCMakeOutFname, fs) 170 qcmd := mocks.NewQueryCommand(t) 171 qcmd.On("Read", mock.Anything).Return(make([]byte, 50), skerr.Fmt("empty data")) 172 err := e.Export(qcmd) 173 assert.Error(t, err) 174} 175 176func TestExport_ValidProtobuf_Success(t *testing.T) { 177 protoData, err := textProtoToProtobuf(textProto) 178 require.NoError(t, err) 179 180 var contents bytes.Buffer 181 fs := mocks.NewFileSystem(t) 182 fs.On("OpenFile", mock.Anything).Once().Run(func(args mock.Arguments) { 183 assert.True(t, filepath.IsAbs(args.String(0))) 184 assert.Equal(t, args.String(0), testCMakeOutFname) 185 }).Return(&contents, nil) 186 e := NewCMakeExporter("projName", testWorkspaceDir, testCMakeOutFname, fs) 187 qcmd := mocks.NewQueryCommand(t) 188 qcmd.On("Read", mock.Anything).Return(protoData, nil) 189 err = e.Export(qcmd) 190 require.NoError(t, err) 191 192 // This expected CMake output text is created by hand. 193 const expected = `# DO NOT EDIT: This file is auto-generated. 194cmake_minimum_required(VERSION 3.13) 195 196project(projName LANGUAGES C CXX) 197 198set(DEFAULT_COMPILE_FLAGS_MACOS "-std=c++17 -Wno-psabi --target=arm64-apple-macos11") 199set(DEFAULT_COMPILE_FLAGS_LINUX "-std=c++17 -Wno-psabi -Wno-attributes") 200 201if (APPLE) 202 set(DEFAULT_COMPILE_FLAGS "${DEFAULT_COMPILE_FLAGS_MACOS}") 203else() 204 set(DEFAULT_COMPILE_FLAGS "${DEFAULT_COMPILE_FLAGS_LINUX}") 205endif() 206 207 208# //src/apps:hello 209add_executable(src_apps_hello "") 210target_sources(src_apps_hello 211 PRIVATE 212 # Sources: 213 "${CMAKE_SOURCE_DIR}/src/apps/hello-world.cpp" 214) 215set_target_properties(src_apps_hello PROPERTIES COMPILE_FLAGS 216 "${DEFAULT_COMPILE_FLAGS} -O1" 217) 218set_target_properties(src_apps_hello PROPERTIES LINK_FLAGS 219 "-L/app/dir" 220) 221set_target_properties(src_apps_hello PROPERTIES COMPILE_DEFINITIONS 222 "APPDEF;SUMDEF" 223) 224set_target_properties(src_apps_hello PROPERTIES INCLUDE_DIRECTORIES 225 "${CMAKE_SOURCE_DIR}/src/libs;${CMAKE_SOURCE_DIR}" 226) 227 228# //src/libs:sum 229add_library(src_libs_sum "") 230target_sources(src_libs_sum 231 PRIVATE 232 # Sources: 233 "${CMAKE_SOURCE_DIR}/src/libs/sum.cpp" 234 # Headers: 235 "${CMAKE_SOURCE_DIR}/src/libs/sum.h" 236) 237set_target_properties(src_libs_sum PROPERTIES COMPILE_FLAGS 238 "${DEFAULT_COMPILE_FLAGS} -O2" 239) 240set_target_properties(src_libs_sum PROPERTIES LINK_FLAGS 241 "-L/library/dir" 242) 243set_target_properties(src_libs_sum PROPERTIES COMPILE_DEFINITIONS 244 "SUMDEF" 245) 246set_target_properties(src_libs_sum PROPERTIES INCLUDE_DIRECTORIES 247 "${CMAKE_SOURCE_DIR}/src/libs;${CMAKE_SOURCE_DIR}" 248) 249` 250 251 assert.Equal(t, expected, contents.String()) 252} 253 254func TestGetRuleCopts_CoptsExists_Success(t *testing.T) { 255 qr := analysis_v2.CqueryResult{} 256 err := prototext.Unmarshal([]byte(textProto), &qr) 257 require.NoError(t, err) 258 259 r := findRule(&qr, "//src/apps:hello") 260 require.NotNil(t, r) 261 262 copts, err := getRuleCopts(r) 263 require.NoError(t, err) 264 assert.Equal(t, []string{"${DEFAULT_COMPILE_FLAGS}", "-O1"}, copts) 265} 266