| // Copyright 2019 The Goma Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package remoteexec |
| |
| import ( |
| "bytes" |
| "context" |
| "errors" |
| "fmt" |
| "sort" |
| "sync" |
| "testing" |
| |
| "github.com/golang/protobuf/proto" |
| "github.com/google/go-cmp/cmp" |
| |
| "go.chromium.org/goma/server/hash" |
| "go.chromium.org/goma/server/log" |
| gomapb "go.chromium.org/goma/server/proto/api" |
| "go.chromium.org/goma/server/remoteexec/digest" |
| "go.chromium.org/goma/server/remoteexec/merkletree" |
| ) |
| |
| func TestSortMissing(t *testing.T) { |
| inputs := []*gomapb.ExecReq_Input{ |
| { |
| Filename: proto.String("../src/hello.cc"), |
| HashKey: proto.String("hash-hello.cc"), |
| }, |
| { |
| Filename: proto.String("../include/base.h"), |
| HashKey: proto.String("hash-base.h"), |
| }, |
| { |
| Filename: proto.String("../include/hello.h"), |
| HashKey: proto.String("hash-hello.h"), |
| }, |
| } |
| |
| resp := &gomapb.ExecResp{ |
| MissingInput: []string{ |
| "../include/hello.h", |
| "../src/hello.cc", |
| }, |
| MissingReason: []string{ |
| "missing-hello.h", |
| "missing-hello.cc", |
| }, |
| } |
| |
| sortMissing(inputs, resp) |
| want := &gomapb.ExecResp{ |
| MissingInput: []string{ |
| "../src/hello.cc", |
| "../include/hello.h", |
| }, |
| MissingReason: []string{ |
| "missing-hello.cc", |
| "missing-hello.h", |
| }, |
| } |
| if !proto.Equal(resp, want) { |
| t.Errorf("sortMissing: %s != %s", resp, want) |
| } |
| |
| resp = proto.Clone(want).(*gomapb.ExecResp) |
| sortMissing(inputs, resp) |
| if !proto.Equal(resp, want) { |
| t.Errorf("sortMissing (stable): %s != %s", resp, want) |
| } |
| } |
| |
| func TestExtractLLVMError(t *testing.T) { |
| // from fdf0c2b3d3d633787324c90ff55404cd0f45fa83cd48e033c332961c9e13e7b5-277800 |
| // in http://b/145177862 |
| msg := []byte(`Note: including file: ../..\content/renderer/render_frame_impl.h |
| Note: including file: ../../buildtools/third_party/libc++/trunk/include\stddef.h |
| Note: including file: ../../buildtools/third_party/libc++/trunk/include\__config |
| Note: including file: ../..\base/native_library.h |
| Note: including file: ../..\ppapi/c/ppb_core.h |
| Note: including file: ../..\ppapi/shared_impl/ppapi_permissions.h |
| Note: including file: ../..\base/debug/invalid_access_win.h |
| Note: including file: ../..\base/process/kill.h |
| LLVM ERROR: out of memory |
| Stack dump: |
| 0. Program arguments: ..\..\third_party\llvm-build\Release+Asserts\bin\clang-cl.exe -cc1 -triple i386-pc-windows-msvc19.16.0 -emit-obj -disable-free -disable-llvm-verifier -discard-value-names -main-file-name render_frame_impl.cc -mrelocation-model static -mthread-model posix -fmerge-all-constants -mframe-pointer=all -relaxed-aliasing -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -target-cpu pentium4 -mllvm -x86-asm-syntax=intel -D_MT -flto-visibility-public-std --dependent-lib=libcmt --dependent-lib=oldnames --show-includes -fno-rtti-data -stack-protector 2 -fms-volatile -fdiagnostics-format msvc -cfguard-no-checks -gcodeview -debug-info-kind=line-tables-only -ffunction-sections -fdata-sections -nostdsysteminc -resource-dir ..\..\third_party\llvm-build\Release+Asserts\lib\clang\10.0.0 -D USE_AURA=1 -D CR_CLANG_REVISION="n331734-e84b7a5f-1" -D _HAS_NODISCARD -D _LIBCPP_ABI_UNSTABLE -D _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D _LIBCPP_ENABLE_NODISCARD -D _LIBCPP_NO_AUTO_LINK -D __STD_C -D _CRT_RAND_S -D _CRT_SECURE_NO_DEPRECATE -D _SCL_SECURE_NO_DEPRECATE -D _ATL_NO_OPENGL -D _WINDOWS -D CERT_CHAIN_PARA_HAS_EXTRA_FIELDS -D PSAPI_VERSION=2 -D WIN32 -D _SECURE_ATL -D _USING_V110_SDK71_ -D WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -D WIN32_LEAN_AND_MEAN -D NOMINMAX -D _UNICODE -D UNICODE -D NTDDI_VERSION=NTDDI_WIN10_RS2 -D _WIN32_WINNT=0x0A00 -D WINVER=0x0A00 -D NDEBUG -D NVALGRIND -D DYNAMIC_ANNOTATIONS_ENABLED=0 -D CONTENT_IMPLEMENTATION -D ENABLE_IPC_FUZZER -D WEBP_EXTERN=extern -D USE_EGL -D _WTL_NO_AUTOMATIC_NAMESPACE -D U_USING_ICU_NAMESPACE=0 -D U_ENABLE_DYLOAD=0 -D USE_CHROMIUM_ICU=1 -D U_STATIC_IMPLEMENTATION -D ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE -D UCHAR_TYPE=wchar_t -D GOOGLE_PROTOBUF_NO_RTTI -D GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER -D WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=0 -D WEBRTC_CHROMIUM_BUILD -D WEBRTC_WIN -D ABSL_ALLOCATOR_NOTHROW=1 -D WEBRTC_USE_BUILTIN_ISAC_FIX=0 -D WEBRTC_USE_BUILTIN_ISAC_FLOAT=1 -D HAVE_SCTP -D NO_MAIN_THREAD_WRAPPING -D SK_HAS_PNG_LIBRARY -D SK_HAS_WEBP_LIBRARY -D SK_USER_CONFIG_HEADER="../../skia/config/SkUserConfig.h" -D SK_GL -D SK_HAS_JPEG_LIBRARY -D SK_HAS_WUFFS_LIBRARY -D SK_SUPPORT_GPU=1 -D SK_GPU_WORKAROUNDS_HEADER="gpu/config/gpu_driver_bug_workaround_autogen.h" -D GR_GL_FUNCTION_TYPE=__stdcall -D LEVELDB_PLATFORM_CHROMIUM=1 -D LEVELDB_PLATFORM_CHROMIUM=1 -D V8_USE_EXTERNAL_STARTUP_DATA -D V8_31BIT_SMIS_ON_64BIT_ARCH -D V8_DEPRECATION_WARNINGS -D SUPPORT_WEBGL2_COMPUTE_CONTEXT=1 -D WTF_USE_WEBAUDIO_PFFFT=1 -D V8_31BIT_SMIS_ON_64BIT_ARCH -D V8_DEPRECATION_WARNINGS -D AUDIO_PROCESSING_IN_AUDIO_SERVICE -D SQLITE_ENABLE_BATCH_ATOMIC_WRITE -D SQLITE_ENABLE_FTS3 -D SQLITE_DISABLE_FTS3_UNICODE -D SQLITE_DISABLE_FTS4_DEFERRED -D SQLITE_ENABLE_ICU -D SQLITE_SECURE_DELETE -D SQLITE_THREADSAFE=1 -D SQLITE_MAX_WORKER_THREADS=0 -D SQLITE_MAX_MMAP_SIZE=268435456 -D SQLITE_DEFAULT_FILE_PERMISSIONS=0600 -D SQLITE_DEFAULT_MEMSTATUS=1 -D SQLITE_DEFAULT_PAGE_SIZE=4096 -D SQLITE_DEFAULT_PCACHE_INITSZ=0 -D SQLITE_LIKE_DOESNT_MATCH_BLOBS -D SQLITE_OMIT_DEPRECATED -D SQLITE_OMIT_PROGRESS_CALLBACK -D SQLITE_OMIT_SHARED_CACHE -D SQLITE_USE_ALLOCA -D SQLITE_OMIT_ANALYZE -D SQLITE_OMIT_AUTOINIT -D SQLITE_OMIT_AUTORESET -D SQLITE_OMIT_COMPILEOPTION_DIAGS -D SQLITE_OMIT_COMPLETE -D SQLITE_OMIT_DECLTYPE -D SQLITE_OMIT_EXPLAIN -D SQLITE_OMIT_GET_TABLE -D SQLITE_OMIT_LOAD_EXTENSION -D SQLITE_DEFAULT_LOOKASIDE=0,0 -D SQLITE_OMIT_LOOKASIDE -D SQLITE_OMIT_TCL_VARIABLE -D SQLITE_OMIT_REINDEX -D SQLITE_OMIT_TRACE -D SQLITE_OMIT_UPSERT -D SQLITE_OMIT_WINDOWFUNC -D SQLITE_HAVE_ISNAN -D SQLITE_TEMP_STORE=3 -D SQLITE_ENABLE_LOCKING_STYLE=0 -I ../.. -I gen -I ../../third_party/libyuv/include -I ../../third_party/jsoncpp/source/include -I ../../third_party/jsoncpp/generated -I ../../third_party/libwebp/src -I ../../third_party/wtl/include -I ../../third_party/khronos -I ../../gpu -I gen/third_party/dawn/src/include -I ../../third_party/dawn/src/include -I ../../third_party/boringssl/src/include -I ../../third_party/ced/src -I ../../third_party/icu/source/common -I ../../third_party/icu/source/i18n -I ../../third_party/protobuf/src -I gen/protoc_out -I ../../third_party/protobuf/src -I ../../third_party/webrtc_overrides -I ../../third_party/webrtc -I gen/third_party/webrtc -I ../../third_party/abseil-cpp -I ../../third_party/skia -I ../../third_party/wuffs/src/release/c -I ../../third_party/libwebm/source -I ../../third_party/leveldatabase -I ../../third_party/leveldatabase/src -I ../../third_party/leveldatabase/src/include -I ../../v8/include -I gen/v8/include -I gen/third_party/metrics_proto -I ../../third_party/mesa_headers -I ../../v8/include -I gen/v8/include -I ../../third_party/perfetto/include -I gen/third_party/perfetto/build_config -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I ../../third_party/libvpx/source/libvpx -I ../../third_party/opus/src/include -D __DATE__= -D __TIME__= -D __TIMESTAMP__= -I ../../buildtools/third_party/libc++/trunk/include -internal-isystem ..\..\third_party\llvm-build\Release+Asserts\lib\clang\10.0.0\include -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\win_sdk\Include\10.0.18362.0\um -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\win_sdk\Include\10.0.18362.0\shared -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\win_sdk\Include\10.0.18362.0\winrt -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\win_sdk\Include\10.0.18362.0\ucrt -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\VC\Tools\MSVC\14.23.28105\include -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\VC\Tools\MSVC\14.23.28105\atlmfc\include -Os -Wno-builtin-macro-redefined -WCL4 -Wimplicit-fallthrough -Wthread-safety -Wextra-semi -Werror -Wno-unused-parameter -Wno-deprecated-declarations -Wno-missing-field-initializers -Wno-unused-parameter -Wno-c++11-narrowing -Wno-unneeded-internal-declaration -Wno-undefined-var-template -Wno-nonportable-include-path -Wno-ignored-pragma-optimize -Wno-implicit-int-float-conversion -Wno-c99-designator -Wno-final-dtor-non-final-class -Wno-sizeof-array-div -Wno-bitwise-conditional-parentheses -Wno-builtin-assume-aligned-alignment -Wno-tautological-bitwise-compare -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -Wshadow -Wexit-time-destructors -Wno-deprecated-declarations -Wno-deprecated-declarations -fdeprecated-macro -fdebug-compilation-dir C:\botcode\w\out\Release -ferror-limit 19 -fmessage-length 0 -fno-use-cxa-atexit -fms-extensions -fms-compatibility -fms-compatibility-version=19.16 -std=c++14 -finline-functions -fobjc-runtime=gcc -fdiagnostics-show-option -fcolor-diagnostics -vectorize-loops -vectorize-slp -mllvm -instcombine-lower-dbg-declare=0 -fdebug-compilation-dir . -add-plugin find-bad-constructs -add-plugin blink-gc-plugin -plugin-arg-blink-gc-plugin no-gc-finalized -fcomplete-member-pointers -faddrsig -o obj/content/renderer/renderer/render_frame_impl.obj -x c++ ../../content/renderer/render_frame_impl.cc |
| 1. <eof> parser at end of file |
| 2. Per-file LLVM IR generation |
| 3. ../../buildtools/third_party/libc++/trunk/include\vector:353:14: Generating code for declaration 'std::__1::__vector_base<content::FaviconURL, std::__1::allocator<content::FaviconURL> >::__end_cap' |
| 0x00007FF633872FE6 (0x00007FF636C42730 0x00007FF635EB0D63 0x000000EB03B89D98 0x000000000000001A) <unknown module> |
| 0x00007FF635EB0C19 (0x00007FF635EB0D63 0x000000EB03B89D98 0x000000000000001A 0x00007FF636C42B30) <unknown module> |
| 0x00007FF636C42730 (0x000000EB03B89D98 0x000000000000001A 0x00007FF636C42B30 0x0000000000000012) <unknown module> |
| 0x00007FF635EB0D63 (0x000000000000001A 0x00007FF636C42B30 0x0000000000000012 0x0000000000000000) <unknown module> |
| 0x000000EB03B89D98 (0x00007FF636C42B30 0x0000000000000012 0x0000000000000000 0x00007FF635EB080D) <unknown module> |
| 0x000000000000001A (0x0000000000000012 0x0000000000000000 0x00007FF635EB080D 0x0000023A0CCA2868) <unknown module> |
| 0x00007FF636C42B30 (0x0000000000000000 0x00007FF635EB080D 0x0000023A0CCA2868 0x000000EB03B8A0C0) <unknown module> |
| 0x0000000000000012 (0x00007FF635EB080D 0x0000023A0CCA2868 0x000000EB03B8A0C0 0x0000000000000000) <unknown module> |
| clang-cl: error: clang frontend command failed due to signal (use -v to see invocation) |
| clang version 10.0.0 (https://github.com/llvm/llvm-project/ e84b7a5fe230e42b8e6fe451369874a773bf1867) |
| Target: i386-pc-windows-msvc |
| Thread model: posix |
| InstalledDir: ..\..\third_party\llvm-build\Release+Asserts\bin |
| clang-cl: note: diagnostic msg: PLEASE submit a bug report to https://crbug.com and run tools/clang/scripts/process_crashreports.py (only works inside Google) which will upload a report and include the crash backtrace, preprocessed source, and associated run script. |
| clang-cl: note: diagnostic msg: |
| ******************** |
| |
| PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT: |
| Preprocessed source(s) and associated run script(s) are located at: |
| clang-cl: note: diagnostic msg: ../../tools/clang/crashreports\render_frame_impl-673e3a.cpp |
| clang-cl: note: diagnostic msg: ../../tools/clang/crashreports\render_frame_impl-673e3a.sh |
| clang-cl: note: diagnostic msg: |
| |
| ******************** |
| `) |
| llvmErrorMsg, ok := extractLLVMError(msg) |
| if got, want := llvmErrorMsg, []byte(`LLVM ERROR: out of memory`); !ok || !bytes.Equal(got, want) { |
| t.Errorf("extractLLVMError(msg)=%q, %t; want=%q; true", |
| string(got), ok, string(want)) |
| } |
| |
| } |
| |
| func TestChangeSymlinkAbsToRel(t *testing.T) { |
| for _, tc := range []struct { |
| desc string |
| name string |
| target string |
| wantTarget string |
| wantErr bool |
| }{ |
| { |
| desc: "base", |
| name: "/a/b.txt", |
| target: "/c/d.txt", |
| wantTarget: "../c/d.txt", |
| }, |
| { |
| desc: "should be error because name is not absolute", |
| name: "../a/b.txt", |
| target: "/c/d.txt", |
| wantErr: true, |
| }, |
| { |
| desc: "should allow name is in root", |
| name: "/a.txt", |
| target: "/c/d.txt", |
| wantTarget: "c/d.txt", |
| }, |
| } { |
| t.Run(tc.desc, func(t *testing.T) { |
| e := merkletree.Entry{ |
| Name: tc.name, |
| Target: tc.target, |
| } |
| actual, err := changeSymlinkAbsToRel(e) |
| if tc.wantErr && err == nil { |
| t.Errorf("changeSymlinkAbsToRel(%v) returned nil err; want err", e) |
| } |
| if !tc.wantErr && err != nil { |
| t.Errorf("changeSymlinkAbsToRel(%v) returned %v ; want nil", e, err) |
| } |
| if tc.wantTarget != actual.Target { |
| expected := merkletree.Entry{ |
| Name: e.Name, |
| Target: tc.wantTarget, |
| } |
| t.Errorf("changeSymlinkAbsToRel(%v) = %v; want %v", e, actual, expected) |
| } |
| }) |
| } |
| } |
| |
| type fakeGomaInput struct { |
| mu sync.Mutex |
| digests map[*gomapb.ExecReq_Input]digest.Data |
| hashes map[*gomapb.FileBlob]string |
| uploaded []string |
| numUploads int |
| } |
| |
| func (f *fakeGomaInput) setInputs(inputs []*gomapb.ExecReq_Input) { |
| f.mu.Lock() |
| defer f.mu.Unlock() |
| if f.digests == nil { |
| f.digests = make(map[*gomapb.ExecReq_Input]digest.Data) |
| } |
| if f.hashes == nil { |
| f.hashes = make(map[*gomapb.FileBlob]string) |
| } |
| for _, input := range inputs { |
| f.digests[input] = digest.Bytes(input.GetFilename(), input.Content.GetContent()) |
| f.hashes[input.Content] = input.GetHashKey() |
| } |
| } |
| |
| func (f *fakeGomaInput) toDigest(ctx context.Context, in *gomapb.ExecReq_Input) (digest.Data, error) { |
| d, ok := f.digests[in] |
| if !ok { |
| return nil, errors.New("not found") |
| } |
| return d, nil |
| } |
| |
| func (f *fakeGomaInput) upload(ctx context.Context, blobs []*gomapb.FileBlob) ([]string, error) { |
| f.mu.Lock() |
| defer f.mu.Unlock() |
| hashes := make([]string, 0, len(blobs)) |
| for _, blob := range blobs { |
| h, ok := f.hashes[blob] |
| if !ok { |
| return nil, errors.New("upload error") |
| } |
| f.uploaded = append(f.uploaded, h) |
| f.numUploads++ |
| hashes = append(hashes, h) |
| } |
| return hashes, nil |
| } |
| |
| func makeInput(tb testing.TB, content, filename string) *gomapb.ExecReq_Input { |
| tb.Helper() |
| blob := &gomapb.FileBlob{ |
| BlobType: gomapb.FileBlob_FILE.Enum(), |
| Content: []byte(content), |
| FileSize: proto.Int64(int64(len(content))), |
| } |
| hashkey, err := hash.SHA256Proto(blob) |
| if err != nil { |
| tb.Fatal(err) |
| } |
| return &gomapb.ExecReq_Input{ |
| HashKey: proto.String(hashkey), |
| Filename: proto.String(filename), |
| Content: blob, |
| } |
| } |
| |
| func TestUploadInputFiles(t *testing.T) { |
| sema := make(chan struct{}, 3) |
| |
| inputs := make([]*gomapb.ExecReq_Input, 6) |
| for i := range inputs { |
| inputs[i] = makeInput(t, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i)) |
| } |
| |
| manyInputs := make([]*gomapb.ExecReq_Input, 1000) |
| for i := range manyInputs { |
| manyInputs[i] = makeInput(t, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i)) |
| } |
| |
| makeHashKeys := func(inputs []*gomapb.ExecReq_Input) []string { |
| result := make([]string, len(inputs)) |
| for i, input := range inputs { |
| result[i] = input.GetHashKey() |
| } |
| return result |
| } |
| |
| for _, tc := range []struct { |
| desc string |
| stored []*gomapb.ExecReq_Input |
| inputs []*gomapb.ExecReq_Input |
| wantUploaded []string |
| wantNumUploads int |
| wantErr bool |
| }{ |
| { |
| desc: "uploads", |
| stored: inputs, |
| inputs: inputs, |
| wantUploaded: makeHashKeys(inputs), |
| wantNumUploads: len(inputs), |
| }, { |
| desc: "many uploads", |
| stored: manyInputs, |
| inputs: manyInputs, |
| wantUploaded: makeHashKeys(manyInputs), |
| wantNumUploads: len(manyInputs), |
| }, { |
| desc: "all errors", |
| inputs: inputs, |
| wantErr: true, |
| }, { |
| desc: "partial errors", |
| stored: inputs[:3], |
| inputs: inputs, |
| wantUploaded: makeHashKeys(manyInputs[:3]), |
| wantNumUploads: len(inputs[:3]), |
| wantErr: true, |
| }, |
| } { |
| t.Run(tc.desc, func(t *testing.T) { |
| gi := &fakeGomaInput{} |
| ctx := context.Background() |
| |
| gi.setInputs(tc.stored) |
| |
| err := uploadInputFiles(ctx, tc.inputs, gi, sema) |
| |
| sort.Strings(gi.uploaded) |
| sort.Strings(tc.wantUploaded) |
| if !cmp.Equal(gi.uploaded, tc.wantUploaded) { |
| t.Errorf("gi.uploaded -want +got: %s", cmp.Diff(tc.wantUploaded, gi.uploaded)) |
| } |
| if gi.numUploads != tc.wantNumUploads { |
| t.Errorf("numUploads=%d; want %d", gi.numUploads, tc.wantNumUploads) |
| } |
| if err != nil && !tc.wantErr { |
| t.Errorf("err=%v; want nil", err) |
| } |
| }) |
| } |
| } |
| |
| func TestInputFiles(t *testing.T) { |
| sema := make(chan struct{}, 3) |
| |
| // These are minimal function / map that are not being tested. |
| rootRel := func(filename string) (string, error) { return filename, nil } |
| executableInputs := map[string]bool{} |
| |
| inputs := make([]*gomapb.ExecReq_Input, 6) |
| inputsNoContent := make([]*gomapb.ExecReq_Input, len(inputs)) |
| for i := range inputs { |
| input := makeInput(t, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i)) |
| inputs[i] = input |
| inputsNoContent[i] = &gomapb.ExecReq_Input{ |
| HashKey: input.HashKey, |
| Filename: input.Filename, |
| } |
| } |
| |
| manyInputs := make([]*gomapb.ExecReq_Input, 1000) |
| for i := range manyInputs { |
| manyInputs[i] = makeInput(t, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i)) |
| } |
| |
| makeMissing := func(input *gomapb.ExecReq_Input) inputFileResult { |
| return inputFileResult{ |
| missingInput: input.GetFilename(), |
| missingReason: "input: not found", |
| } |
| } |
| makeFound := func(input *gomapb.ExecReq_Input) inputFileResult { |
| return inputFileResult{ |
| file: merkletree.Entry{ |
| Name: input.GetFilename(), // Because `rootRel()` is an identity function |
| Data: inputDigestData{ |
| filename: input.GetFilename(), |
| Data: digest.Bytes(input.GetFilename(), input.Content.GetContent()), |
| }, |
| }, |
| uploaded: input.Content != nil, |
| } |
| } |
| |
| for _, tc := range []struct { |
| desc string |
| stored []*gomapb.ExecReq_Input |
| inputs []*gomapb.ExecReq_Input |
| wantResult []inputFileResult |
| }{ |
| { |
| desc: "all missing", |
| inputs: inputsNoContent, |
| wantResult: []inputFileResult{ |
| makeMissing(inputs[0]), |
| makeMissing(inputs[1]), |
| makeMissing(inputs[2]), |
| makeMissing(inputs[3]), |
| makeMissing(inputs[4]), |
| makeMissing(inputs[5]), |
| }, |
| }, { |
| desc: "new content but not stored", |
| inputs: inputs, |
| wantResult: []inputFileResult{ |
| makeMissing(inputs[0]), |
| makeMissing(inputs[1]), |
| makeMissing(inputs[2]), |
| makeMissing(inputs[3]), |
| makeMissing(inputs[4]), |
| makeMissing(inputs[5]), |
| }, |
| }, { |
| desc: "all stored no content", |
| stored: inputsNoContent, |
| inputs: inputsNoContent, |
| wantResult: []inputFileResult{ |
| makeFound(inputsNoContent[0]), |
| makeFound(inputsNoContent[1]), |
| makeFound(inputsNoContent[2]), |
| makeFound(inputsNoContent[3]), |
| makeFound(inputsNoContent[4]), |
| makeFound(inputsNoContent[5]), |
| }, |
| }, { |
| desc: "all uploaded", |
| stored: inputs, |
| inputs: inputs, |
| wantResult: []inputFileResult{ |
| makeFound(inputs[0]), |
| makeFound(inputs[1]), |
| makeFound(inputs[2]), |
| makeFound(inputs[3]), |
| makeFound(inputs[4]), |
| makeFound(inputs[5]), |
| }, |
| }, |
| { |
| desc: "mixed", |
| stored: []*gomapb.ExecReq_Input{ |
| inputs[0], |
| inputsNoContent[1], |
| inputs[3], |
| inputsNoContent[4], |
| }, |
| inputs: []*gomapb.ExecReq_Input{ |
| inputs[0], |
| inputsNoContent[1], |
| inputs[2], |
| inputs[3], |
| inputsNoContent[4], |
| inputsNoContent[5], |
| }, |
| wantResult: []inputFileResult{ |
| makeFound(inputs[0]), |
| makeFound(inputsNoContent[1]), |
| makeMissing(inputs[2]), |
| makeFound(inputs[3]), |
| makeFound(inputsNoContent[4]), |
| makeMissing(inputs[5]), |
| }, |
| }, { |
| desc: "many uploads", |
| stored: manyInputs, |
| inputs: manyInputs, |
| wantResult: func() []inputFileResult { |
| result := make([]inputFileResult, len(manyInputs)) |
| for i, input := range manyInputs { |
| result[i] = makeFound(input) |
| } |
| return result |
| }(), |
| }, |
| } { |
| t.Run(tc.desc, func(t *testing.T) { |
| gi := &fakeGomaInput{} |
| gi.setInputs(tc.stored) |
| ctx := context.Background() |
| |
| results := inputFiles(ctx, tc.inputs, gi, rootRel, executableInputs, sema) |
| |
| digestDataComparer := cmp.Comparer(func(x, y digest.Data) bool { |
| if x == nil && y == nil { |
| return true |
| } |
| if x == nil || y == nil { |
| return false |
| } |
| return proto.Equal(x.Digest(), y.Digest()) && x.String() == y.String() |
| }) |
| |
| if !cmp.Equal(results, tc.wantResult, cmp.AllowUnexported(inputFileResult{}), cmp.AllowUnexported(inputDigestData{}), digestDataComparer) { |
| t.Errorf("results=%v; want %v", results, tc.wantResult) |
| } |
| }) |
| } |
| } |
| |
| type nopLogger struct{} |
| |
| func (nopLogger) Debug(args ...interface{}) {} |
| func (nopLogger) Debugf(format string, arg ...interface{}) {} |
| func (nopLogger) Info(args ...interface{}) {} |
| func (nopLogger) Infof(format string, arg ...interface{}) {} |
| func (nopLogger) Warn(args ...interface{}) {} |
| func (nopLogger) Warnf(format string, arg ...interface{}) {} |
| func (nopLogger) Error(args ...interface{}) {} |
| func (nopLogger) Errorf(format string, arg ...interface{}) {} |
| func (nopLogger) Fatal(args ...interface{}) {} |
| func (nopLogger) Fatalf(format string, arg ...interface{}) {} |
| func (nopLogger) Sync() error { return nil } |
| |
| func BenchmarkInputFiles(b *testing.B) { |
| var inputs []*gomapb.ExecReq_Input |
| for i := 0; i < 1000; i++ { |
| inputs = append(inputs, makeInput(b, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i))) |
| } |
| gi := &fakeGomaInput{} |
| gi.setInputs(inputs) |
| rootRel := func(filename string) (string, error) { return filename, nil } |
| executableInputs := map[string]bool{} |
| sema := make(chan struct{}, 5) |
| ctx := log.NewContext(context.Background(), nopLogger{}) |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| inputFiles(ctx, inputs, gi, rootRel, executableInputs, sema) |
| } |
| } |