1// Copyright 2022 Google Inc. All rights reserved. 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 15package main 16 17import ( 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 "strings" 22 "testing" 23 24 "android/soong/cmd/symbols_map/symbols_map_proto" 25 26 "google.golang.org/protobuf/encoding/prototext" 27 "google.golang.org/protobuf/proto" 28) 29 30func Test_mergeProtos(t *testing.T) { 31 type testFile struct { 32 filename string 33 contents *symbols_map_proto.Mapping 34 missing bool 35 } 36 37 tests := []struct { 38 name string 39 inputs []testFile 40 stripPrefix string 41 writeIfChanged bool 42 ignoreMissingFiles bool 43 44 error string 45 output *symbols_map_proto.Mappings 46 }{ 47 { 48 name: "empty", 49 output: &symbols_map_proto.Mappings{}, 50 }, 51 { 52 name: "merge", 53 inputs: []testFile{ 54 { 55 filename: "foo", 56 contents: &symbols_map_proto.Mapping{ 57 Identifier: proto.String("foo"), 58 Location: proto.String("symbols/foo"), 59 Type: symbols_map_proto.Mapping_ELF.Enum(), 60 }, 61 }, 62 { 63 filename: "bar", 64 contents: &symbols_map_proto.Mapping{ 65 Identifier: proto.String("bar"), 66 Location: proto.String("symbols/bar"), 67 Type: symbols_map_proto.Mapping_R8.Enum(), 68 }, 69 }, 70 }, 71 output: &symbols_map_proto.Mappings{ 72 Mappings: []*symbols_map_proto.Mapping{ 73 { 74 Identifier: proto.String("foo"), 75 Location: proto.String("symbols/foo"), 76 Type: symbols_map_proto.Mapping_ELF.Enum(), 77 }, 78 { 79 Identifier: proto.String("bar"), 80 Location: proto.String("symbols/bar"), 81 Type: symbols_map_proto.Mapping_R8.Enum(), 82 }, 83 }, 84 }, 85 }, 86 { 87 name: "strip prefix", 88 inputs: []testFile{ 89 { 90 filename: "foo", 91 contents: &symbols_map_proto.Mapping{ 92 Identifier: proto.String("foo"), 93 Location: proto.String("symbols/foo"), 94 Type: symbols_map_proto.Mapping_ELF.Enum(), 95 }, 96 }, 97 { 98 filename: "bar", 99 contents: &symbols_map_proto.Mapping{ 100 Identifier: proto.String("bar"), 101 Location: proto.String("symbols/bar"), 102 Type: symbols_map_proto.Mapping_R8.Enum(), 103 }, 104 }, 105 }, 106 stripPrefix: "symbols/", 107 output: &symbols_map_proto.Mappings{ 108 Mappings: []*symbols_map_proto.Mapping{ 109 { 110 Identifier: proto.String("foo"), 111 Location: proto.String("foo"), 112 Type: symbols_map_proto.Mapping_ELF.Enum(), 113 }, 114 { 115 Identifier: proto.String("bar"), 116 Location: proto.String("bar"), 117 Type: symbols_map_proto.Mapping_R8.Enum(), 118 }, 119 }, 120 }, 121 }, 122 { 123 name: "missing", 124 inputs: []testFile{ 125 { 126 filename: "foo", 127 contents: &symbols_map_proto.Mapping{ 128 Identifier: proto.String("foo"), 129 Location: proto.String("symbols/foo"), 130 Type: symbols_map_proto.Mapping_ELF.Enum(), 131 }, 132 }, 133 { 134 filename: "bar", 135 missing: true, 136 }, 137 }, 138 error: "no such file or directory", 139 }, 140 { 141 name: "ignore missing", 142 inputs: []testFile{ 143 { 144 filename: "foo", 145 contents: &symbols_map_proto.Mapping{ 146 Identifier: proto.String("foo"), 147 Location: proto.String("symbols/foo"), 148 Type: symbols_map_proto.Mapping_ELF.Enum(), 149 }, 150 }, 151 { 152 filename: "bar", 153 missing: true, 154 }, 155 }, 156 ignoreMissingFiles: true, 157 output: &symbols_map_proto.Mappings{ 158 Mappings: []*symbols_map_proto.Mapping{ 159 { 160 Identifier: proto.String("foo"), 161 Location: proto.String("symbols/foo"), 162 Type: symbols_map_proto.Mapping_ELF.Enum(), 163 }, 164 }, 165 }, 166 }, 167 } 168 for _, tt := range tests { 169 t.Run(tt.name, func(t *testing.T) { 170 dir, err := os.MkdirTemp("", "test_mergeProtos") 171 if err != nil { 172 t.Fatalf("failed to create temporary directory: %s", err) 173 } 174 defer os.RemoveAll(dir) 175 176 var inputs []string 177 for _, in := range tt.inputs { 178 path := filepath.Join(dir, in.filename) 179 inputs = append(inputs, path) 180 if !in.missing { 181 err := writeTextProto(path, in.contents, false) 182 if err != nil { 183 t.Fatalf("failed to create input file %s: %s", path, err) 184 } 185 } 186 } 187 output := filepath.Join(dir, "out") 188 189 err = mergeProtos(output, inputs, tt.stripPrefix, tt.writeIfChanged, tt.ignoreMissingFiles) 190 if err != nil { 191 if tt.error != "" { 192 if !strings.Contains(err.Error(), tt.error) { 193 t.Fatalf("expected error %q, got %s", tt.error, err.Error()) 194 } 195 } else { 196 t.Fatalf("unexpected error %q", err) 197 } 198 } else if tt.error != "" { 199 t.Fatalf("missing error %q", tt.error) 200 } else { 201 data, err := ioutil.ReadFile(output) 202 if err != nil { 203 t.Fatalf("failed to read output file %s: %s", output, err) 204 } 205 var got symbols_map_proto.Mappings 206 err = prototext.Unmarshal(data, &got) 207 if err != nil { 208 t.Fatalf("failed to unmarshal textproto %s: %s", output, err) 209 } 210 211 if !proto.Equal(tt.output, &got) { 212 t.Fatalf("expected output %q, got %q", tt.output.String(), got.String()) 213 } 214 } 215 }) 216 } 217} 218