1 // Copyright 2020 Google LLC 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 com.google.api.generator.gapic.protoparser; 16 17 import static org.junit.Assert.assertEquals; 18 import static org.junit.Assert.assertTrue; 19 20 import com.google.api.generator.engine.ast.TypeNode; 21 import com.google.api.generator.engine.ast.VaporReference; 22 import com.google.api.generator.gapic.model.Field; 23 import com.google.api.generator.gapic.model.MethodArgument; 24 import java.util.Arrays; 25 import java.util.HashMap; 26 import java.util.List; 27 import java.util.Map; 28 import java.util.function.BiFunction; 29 import java.util.function.Function; 30 import java.util.stream.Collectors; 31 import org.junit.Test; 32 33 public class MethodSignatureParserTest { 34 35 @Test flattenMethodSignatures_basic()36 public void flattenMethodSignatures_basic() { 37 String fooName = "fooName"; 38 TypeNode fooTypeOne = 39 TypeNode.withReference( 40 VaporReference.builder().setName("FooName").setPakkage("com.google.foobar").build()); 41 TypeNode fooTypeTwo = 42 TypeNode.withReference( 43 VaporReference.builder().setName("FooTwoName").setPakkage("com.google.foobar").build()); 44 45 List<String> argumentNames = Arrays.asList(fooName); 46 47 BiFunction<String, TypeNode, MethodArgument> methodArgFn = 48 (name, type) -> 49 MethodArgument.builder() 50 .setName(name) 51 .setType(type) 52 .setField(Field.builder().setName(name).setType(type).build()) 53 .build(); 54 List<MethodArgument> fooArgs = 55 Arrays.asList(TypeNode.STRING, fooTypeOne, fooTypeTwo).stream() 56 .map(t -> methodArgFn.apply(fooName, t)) 57 .collect(Collectors.toList()); 58 Map<String, List<MethodArgument>> argumentNameToOverloads = new HashMap<>(); 59 argumentNameToOverloads.put(fooName, fooArgs); 60 61 List<List<MethodArgument>> flattenedSignatures = 62 MethodSignatureParser.flattenMethodSignatureVariants( 63 argumentNames, argumentNameToOverloads); 64 65 assertEquals(3, flattenedSignatures.size()); 66 67 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(TypeNode.STRING))); 68 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(fooTypeOne))); 69 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(fooTypeTwo))); 70 } 71 72 @Test flattenMethodSignatures_oneToMany()73 public void flattenMethodSignatures_oneToMany() { 74 String fooName = "fooName"; 75 String anInt = "anInt"; 76 77 TypeNode fooTypeOne = 78 TypeNode.withReference( 79 VaporReference.builder().setName("FooName").setPakkage("com.google.foobar").build()); 80 TypeNode fooTypeTwo = 81 TypeNode.withReference( 82 VaporReference.builder().setName("FooTwoName").setPakkage("com.google.foobar").build()); 83 84 List<String> argumentNames = Arrays.asList(anInt, fooName); 85 86 BiFunction<String, TypeNode, MethodArgument> methodArgFn = 87 (name, type) -> 88 MethodArgument.builder() 89 .setName(name) 90 .setType(type) 91 .setField(Field.builder().setName(name).setType(type).build()) 92 .build(); 93 List<MethodArgument> fooArgs = 94 Arrays.asList(TypeNode.STRING, fooTypeOne, fooTypeTwo).stream() 95 .map(t -> methodArgFn.apply(fooName, t)) 96 .collect(Collectors.toList()); 97 Map<String, List<MethodArgument>> argumentNameToOverloads = new HashMap<>(); 98 argumentNameToOverloads.put(fooName, fooArgs); 99 argumentNameToOverloads.put(anInt, Arrays.asList(methodArgFn.apply(anInt, TypeNode.INT))); 100 101 List<List<MethodArgument>> flattenedSignatures = 102 MethodSignatureParser.flattenMethodSignatureVariants( 103 argumentNames, argumentNameToOverloads); 104 105 assertEquals(3, flattenedSignatures.size()); 106 107 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(TypeNode.INT, TypeNode.STRING))); 108 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(TypeNode.INT, fooTypeOne))); 109 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(TypeNode.INT, fooTypeTwo))); 110 } 111 112 @Test flattenMethodSignatures_manyToOne()113 public void flattenMethodSignatures_manyToOne() { 114 String fooName = "fooName"; 115 String anInt = "anInt"; 116 117 TypeNode fooTypeOne = 118 TypeNode.withReference( 119 VaporReference.builder().setName("FooName").setPakkage("com.google.foobar").build()); 120 TypeNode fooTypeTwo = 121 TypeNode.withReference( 122 VaporReference.builder().setName("FooTwoName").setPakkage("com.google.foobar").build()); 123 124 List<String> argumentNames = Arrays.asList(fooName, anInt); 125 126 BiFunction<String, TypeNode, MethodArgument> methodArgFn = 127 (name, type) -> 128 MethodArgument.builder() 129 .setName(name) 130 .setType(type) 131 .setField(Field.builder().setName(name).setType(type).build()) 132 .build(); 133 List<MethodArgument> fooArgs = 134 Arrays.asList(TypeNode.STRING, fooTypeOne, fooTypeTwo).stream() 135 .map(t -> methodArgFn.apply(fooName, t)) 136 .collect(Collectors.toList()); 137 Map<String, List<MethodArgument>> argumentNameToOverloads = new HashMap<>(); 138 argumentNameToOverloads.put(fooName, fooArgs); 139 argumentNameToOverloads.put(anInt, Arrays.asList(methodArgFn.apply(anInt, TypeNode.INT))); 140 141 List<List<MethodArgument>> flattenedSignatures = 142 MethodSignatureParser.flattenMethodSignatureVariants( 143 argumentNames, argumentNameToOverloads); 144 145 assertEquals(3, flattenedSignatures.size()); 146 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(TypeNode.STRING, TypeNode.INT))); 147 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(fooTypeOne, TypeNode.INT))); 148 149 assertTrue(containsTypes(flattenedSignatures, Arrays.asList(fooTypeTwo, TypeNode.INT))); 150 } 151 152 @Test flattenMethodSignatures_manyToMany()153 public void flattenMethodSignatures_manyToMany() { 154 String fooName = "fooName"; 155 String barName = "barName"; 156 String anInt = "anInt"; 157 String anotherInt = "anotherInt"; 158 159 TypeNode fooTypeOne = 160 TypeNode.withReference( 161 VaporReference.builder().setName("FooName").setPakkage("com.google.foobar").build()); 162 TypeNode fooTypeTwo = 163 TypeNode.withReference( 164 VaporReference.builder().setName("FooTwoName").setPakkage("com.google.foobar").build()); 165 TypeNode barTypeOne = 166 TypeNode.withReference( 167 VaporReference.builder().setName("BarName").setPakkage("com.google.foobar").build()); 168 TypeNode barTypeTwo = 169 TypeNode.withReference( 170 VaporReference.builder().setName("BarTwoName").setPakkage("com.google.foobar").build()); 171 TypeNode barTypeThree = 172 TypeNode.withReference( 173 VaporReference.builder().setName("BarCarName").setPakkage("com.google.foobar").build()); 174 175 List<String> argumentNames = Arrays.asList(fooName, anInt, barName, anotherInt); 176 177 BiFunction<String, TypeNode, MethodArgument> methodArgFn = 178 (name, type) -> 179 MethodArgument.builder() 180 .setName(name) 181 .setType(type) 182 .setField(Field.builder().setName(name).setType(type).build()) 183 .build(); 184 List<MethodArgument> fooArgs = 185 Arrays.asList(TypeNode.STRING, fooTypeOne, fooTypeTwo).stream() 186 .map(t -> methodArgFn.apply(fooName, t)) 187 .collect(Collectors.toList()); 188 List<MethodArgument> barArgs = 189 Arrays.asList(TypeNode.STRING, barTypeOne, barTypeTwo, barTypeThree).stream() 190 .map(t -> methodArgFn.apply(barName, t)) 191 .collect(Collectors.toList()); 192 Map<String, List<MethodArgument>> argumentNameToOverloads = new HashMap<>(); 193 argumentNameToOverloads.put(fooName, fooArgs); 194 argumentNameToOverloads.put(anInt, Arrays.asList(methodArgFn.apply(anInt, TypeNode.INT))); 195 argumentNameToOverloads.put(barName, barArgs); 196 argumentNameToOverloads.put( 197 anotherInt, Arrays.asList(methodArgFn.apply(anotherInt, TypeNode.INT))); 198 199 List<List<MethodArgument>> flattenedSignatures = 200 MethodSignatureParser.flattenMethodSignatureVariants( 201 argumentNames, argumentNameToOverloads); 202 203 assertEquals(12, flattenedSignatures.size()); 204 205 // Types 0 - 4: String, int, {String, BarName, BarTwoName, BarCarName}, int. 206 assertTrue( 207 containsTypes( 208 flattenedSignatures, 209 Arrays.asList(TypeNode.STRING, TypeNode.INT, TypeNode.STRING, TypeNode.INT))); 210 assertTrue( 211 containsTypes( 212 flattenedSignatures, 213 Arrays.asList(TypeNode.STRING, TypeNode.INT, barTypeOne, TypeNode.INT))); 214 assertTrue( 215 containsTypes( 216 flattenedSignatures, 217 Arrays.asList(TypeNode.STRING, TypeNode.INT, barTypeTwo, TypeNode.INT))); 218 assertTrue( 219 containsTypes( 220 flattenedSignatures, 221 Arrays.asList(TypeNode.STRING, TypeNode.INT, barTypeThree, TypeNode.INT))); 222 223 // Types 5 - 8: FooName, int, {String, BarName, BarTwoName, BarCarName}, int. 224 assertTrue( 225 containsTypes( 226 flattenedSignatures, 227 Arrays.asList(fooTypeOne, TypeNode.INT, TypeNode.STRING, TypeNode.INT))); 228 assertTrue( 229 containsTypes( 230 flattenedSignatures, 231 Arrays.asList(fooTypeOne, TypeNode.INT, barTypeOne, TypeNode.INT))); 232 assertTrue( 233 containsTypes( 234 flattenedSignatures, 235 Arrays.asList(fooTypeOne, TypeNode.INT, barTypeTwo, TypeNode.INT))); 236 assertTrue( 237 containsTypes( 238 flattenedSignatures, 239 Arrays.asList(fooTypeOne, TypeNode.INT, barTypeThree, TypeNode.INT))); 240 241 // Types 9 - 12: FooTwoName, int, {String, BarName, BarTwoName, BarCarName}, int. 242 assertTrue( 243 containsTypes( 244 flattenedSignatures, 245 Arrays.asList(fooTypeTwo, TypeNode.INT, TypeNode.STRING, TypeNode.INT))); 246 assertTrue( 247 containsTypes( 248 flattenedSignatures, 249 Arrays.asList(fooTypeTwo, TypeNode.INT, barTypeOne, TypeNode.INT))); 250 assertTrue( 251 containsTypes( 252 flattenedSignatures, 253 Arrays.asList(fooTypeTwo, TypeNode.INT, barTypeTwo, TypeNode.INT))); 254 assertTrue( 255 containsTypes( 256 flattenedSignatures, 257 Arrays.asList(fooTypeTwo, TypeNode.INT, barTypeThree, TypeNode.INT))); 258 } 259 containsTypes( List<List<MethodArgument>> flattenedSignatures, List<TypeNode> types)260 private static boolean containsTypes( 261 List<List<MethodArgument>> flattenedSignatures, List<TypeNode> types) { 262 // Brute-force search. Feel free to improve this if you've got cycles . 263 Function<List<MethodArgument>, List<TypeNode>> typeExtractorFn = 264 methodArgs -> methodArgs.stream().map(m -> m.type()).collect(Collectors.toList()); 265 for (List<MethodArgument> args : flattenedSignatures) { 266 if (typeExtractorFn.apply(args).equals(types)) { 267 return true; 268 } 269 } 270 return false; 271 } 272 } 273