1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements.  See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership.  The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License.  You may obtain a copy of the License at
8 //
9 //   http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied.  See the License for the
15 // specific language governing permissions and limitations
16 // under the License.
17 
18 #include <memory>
19 #include <string>
20 #include <vector>
21 
22 #include <gtest/gtest.h>
23 
24 #include "arrow/compute/kernel.h"
25 #include "arrow/status.h"
26 #include "arrow/testing/gtest_util.h"
27 #include "arrow/type.h"
28 #include "arrow/util/key_value_metadata.h"
29 
30 namespace arrow {
31 namespace compute {
32 
33 // ----------------------------------------------------------------------
34 // TypeMatcher
35 
TEST(TypeMatcher,SameTypeId)36 TEST(TypeMatcher, SameTypeId) {
37   std::shared_ptr<TypeMatcher> matcher = match::SameTypeId(Type::DECIMAL);
38   ASSERT_TRUE(matcher->Matches(*decimal(12, 2)));
39   ASSERT_FALSE(matcher->Matches(*int8()));
40 
41   ASSERT_EQ("Type::DECIMAL128", matcher->ToString());
42 
43   ASSERT_TRUE(matcher->Equals(*matcher));
44   ASSERT_TRUE(matcher->Equals(*match::SameTypeId(Type::DECIMAL)));
45   ASSERT_FALSE(matcher->Equals(*match::SameTypeId(Type::TIMESTAMP)));
46 }
47 
TEST(TypeMatcher,TimestampTypeUnit)48 TEST(TypeMatcher, TimestampTypeUnit) {
49   auto matcher = match::TimestampTypeUnit(TimeUnit::MILLI);
50   auto matcher2 = match::Time32TypeUnit(TimeUnit::MILLI);
51 
52   ASSERT_TRUE(matcher->Matches(*timestamp(TimeUnit::MILLI)));
53   ASSERT_TRUE(matcher->Matches(*timestamp(TimeUnit::MILLI, "utc")));
54   ASSERT_FALSE(matcher->Matches(*timestamp(TimeUnit::SECOND)));
55   ASSERT_FALSE(matcher->Matches(*time32(TimeUnit::MILLI)));
56   ASSERT_TRUE(matcher2->Matches(*time32(TimeUnit::MILLI)));
57 
58   // Check ToString representation
59   ASSERT_EQ("timestamp(s)", match::TimestampTypeUnit(TimeUnit::SECOND)->ToString());
60   ASSERT_EQ("timestamp(ms)", match::TimestampTypeUnit(TimeUnit::MILLI)->ToString());
61   ASSERT_EQ("timestamp(us)", match::TimestampTypeUnit(TimeUnit::MICRO)->ToString());
62   ASSERT_EQ("timestamp(ns)", match::TimestampTypeUnit(TimeUnit::NANO)->ToString());
63 
64   // Equals implementation
65   ASSERT_TRUE(matcher->Equals(*matcher));
66   ASSERT_TRUE(matcher->Equals(*match::TimestampTypeUnit(TimeUnit::MILLI)));
67   ASSERT_FALSE(matcher->Equals(*match::TimestampTypeUnit(TimeUnit::MICRO)));
68   ASSERT_FALSE(matcher->Equals(*match::Time32TypeUnit(TimeUnit::MILLI)));
69 }
70 
71 // ----------------------------------------------------------------------
72 // InputType
73 
TEST(InputType,AnyTypeConstructor)74 TEST(InputType, AnyTypeConstructor) {
75   // Check the ANY_TYPE ctors
76   InputType ty;
77   ASSERT_EQ(InputType::ANY_TYPE, ty.kind());
78   ASSERT_EQ(ValueDescr::ANY, ty.shape());
79 
80   ty = InputType(ValueDescr::SCALAR);
81   ASSERT_EQ(ValueDescr::SCALAR, ty.shape());
82 
83   ty = InputType(ValueDescr::ARRAY);
84   ASSERT_EQ(ValueDescr::ARRAY, ty.shape());
85 }
86 
TEST(InputType,Constructors)87 TEST(InputType, Constructors) {
88   // Exact type constructor
89   InputType ty1(int8());
90   ASSERT_EQ(InputType::EXACT_TYPE, ty1.kind());
91   ASSERT_EQ(ValueDescr::ANY, ty1.shape());
92   AssertTypeEqual(*int8(), *ty1.type());
93 
94   InputType ty1_implicit = int8();
95   ASSERT_TRUE(ty1.Equals(ty1_implicit));
96 
97   InputType ty1_array(int8(), ValueDescr::ARRAY);
98   ASSERT_EQ(ValueDescr::ARRAY, ty1_array.shape());
99 
100   InputType ty1_scalar(int8(), ValueDescr::SCALAR);
101   ASSERT_EQ(ValueDescr::SCALAR, ty1_scalar.shape());
102 
103   // Same type id constructor
104   InputType ty2(Type::DECIMAL);
105   ASSERT_EQ(InputType::USE_TYPE_MATCHER, ty2.kind());
106   ASSERT_EQ("any[Type::DECIMAL128]", ty2.ToString());
107   ASSERT_TRUE(ty2.type_matcher().Matches(*decimal(12, 2)));
108   ASSERT_FALSE(ty2.type_matcher().Matches(*int16()));
109 
110   InputType ty2_array(Type::DECIMAL, ValueDescr::ARRAY);
111   ASSERT_EQ(ValueDescr::ARRAY, ty2_array.shape());
112 
113   InputType ty2_scalar(Type::DECIMAL, ValueDescr::SCALAR);
114   ASSERT_EQ(ValueDescr::SCALAR, ty2_scalar.shape());
115 
116   // Implicit construction in a vector
117   std::vector<InputType> types = {int8(), InputType(Type::DECIMAL)};
118   ASSERT_TRUE(types[0].Equals(ty1));
119   ASSERT_TRUE(types[1].Equals(ty2));
120 
121   // Copy constructor
122   InputType ty3 = ty1;
123   InputType ty4 = ty2;
124   ASSERT_TRUE(ty3.Equals(ty1));
125   ASSERT_TRUE(ty4.Equals(ty2));
126 
127   // Move constructor
128   InputType ty5 = std::move(ty3);
129   InputType ty6 = std::move(ty4);
130   ASSERT_TRUE(ty5.Equals(ty1));
131   ASSERT_TRUE(ty6.Equals(ty2));
132 
133   // ToString
134   ASSERT_EQ("any[int8]", ty1.ToString());
135   ASSERT_EQ("array[int8]", ty1_array.ToString());
136   ASSERT_EQ("scalar[int8]", ty1_scalar.ToString());
137 
138   ASSERT_EQ("any[Type::DECIMAL128]", ty2.ToString());
139   ASSERT_EQ("array[Type::DECIMAL128]", ty2_array.ToString());
140   ASSERT_EQ("scalar[Type::DECIMAL128]", ty2_scalar.ToString());
141 
142   InputType ty7(match::TimestampTypeUnit(TimeUnit::MICRO));
143   ASSERT_EQ("any[timestamp(us)]", ty7.ToString());
144 
145   InputType ty8;
146   InputType ty9(ValueDescr::ANY);
147   InputType ty10(ValueDescr::ARRAY);
148   InputType ty11(ValueDescr::SCALAR);
149   ASSERT_EQ("any[any]", ty8.ToString());
150   ASSERT_EQ("any[any]", ty9.ToString());
151   ASSERT_EQ("array[any]", ty10.ToString());
152   ASSERT_EQ("scalar[any]", ty11.ToString());
153 }
154 
TEST(InputType,Equals)155 TEST(InputType, Equals) {
156   InputType t1 = int8();
157   InputType t2 = int8();
158   InputType t3(int8(), ValueDescr::ARRAY);
159   InputType t3_i32(int32(), ValueDescr::ARRAY);
160   InputType t3_scalar(int8(), ValueDescr::SCALAR);
161   InputType t4(int8(), ValueDescr::ARRAY);
162   InputType t4_i32(int32(), ValueDescr::ARRAY);
163 
164   InputType t5(Type::DECIMAL);
165   InputType t6(Type::DECIMAL);
166   InputType t7(Type::DECIMAL, ValueDescr::SCALAR);
167   InputType t7_i32(Type::INT32, ValueDescr::SCALAR);
168   InputType t8(Type::DECIMAL, ValueDescr::SCALAR);
169   InputType t8_i32(Type::INT32, ValueDescr::SCALAR);
170 
171   ASSERT_TRUE(t1.Equals(t2));
172   ASSERT_EQ(t1, t2);
173 
174   // ANY vs SCALAR
175   ASSERT_NE(t1, t3);
176 
177   ASSERT_EQ(t3, t4);
178 
179   // both ARRAY, but different type
180   ASSERT_NE(t3, t3_i32);
181 
182   // ARRAY vs SCALAR
183   ASSERT_NE(t3, t3_scalar);
184 
185   ASSERT_EQ(t3_i32, t4_i32);
186 
187   ASSERT_FALSE(t1.Equals(t5));
188   ASSERT_NE(t1, t5);
189 
190   ASSERT_EQ(t5, t5);
191   ASSERT_EQ(t5, t6);
192   ASSERT_NE(t5, t7);
193   ASSERT_EQ(t7, t8);
194   ASSERT_EQ(t7, t8);
195   ASSERT_NE(t7, t7_i32);
196   ASSERT_EQ(t7_i32, t8_i32);
197 
198   // NOTE: For the time being, we treat int32() and Type::INT32 as being
199   // different. This could obviously be fixed later to make these equivalent
200   ASSERT_NE(InputType(int8()), InputType(Type::INT32));
201 
202   // Check that field metadata excluded from equality checks
203   InputType t9 = list(
204       field("item", utf8(), /*nullable=*/true, key_value_metadata({"foo"}, {"bar"})));
205   InputType t10 = list(field("item", utf8()));
206   ASSERT_TRUE(t9.Equals(t10));
207 }
208 
TEST(InputType,Hash)209 TEST(InputType, Hash) {
210   InputType t0;
211   InputType t0_scalar(ValueDescr::SCALAR);
212   InputType t0_array(ValueDescr::ARRAY);
213 
214   InputType t1 = int8();
215   InputType t2(Type::DECIMAL);
216 
217   // These checks try to determine first of all whether Hash always returns the
218   // same value, and whether the elements of the type are all incorporated into
219   // the Hash
220   ASSERT_EQ(t0.Hash(), t0.Hash());
221   ASSERT_NE(t0.Hash(), t0_scalar.Hash());
222   ASSERT_NE(t0.Hash(), t0_array.Hash());
223   ASSERT_NE(t0_scalar.Hash(), t0_array.Hash());
224 
225   ASSERT_EQ(t1.Hash(), t1.Hash());
226   ASSERT_EQ(t2.Hash(), t2.Hash());
227 
228   ASSERT_NE(t0.Hash(), t1.Hash());
229   ASSERT_NE(t0.Hash(), t2.Hash());
230   ASSERT_NE(t1.Hash(), t2.Hash());
231 }
232 
TEST(InputType,Matches)233 TEST(InputType, Matches) {
234   InputType ty1 = int8();
235 
236   ASSERT_TRUE(ty1.Matches(ValueDescr::Scalar(int8())));
237   ASSERT_TRUE(ty1.Matches(ValueDescr::Array(int8())));
238   ASSERT_TRUE(ty1.Matches(ValueDescr::Any(int8())));
239   ASSERT_FALSE(ty1.Matches(ValueDescr::Any(int16())));
240 
241   InputType ty2(Type::DECIMAL);
242   ASSERT_TRUE(ty2.Matches(ValueDescr::Scalar(decimal(12, 2))));
243   ASSERT_TRUE(ty2.Matches(ValueDescr::Array(decimal(12, 2))));
244   ASSERT_FALSE(ty2.Matches(ValueDescr::Any(float64())));
245 
246   InputType ty3(int64(), ValueDescr::SCALAR);
247   ASSERT_FALSE(ty3.Matches(ValueDescr::Array(int64())));
248   ASSERT_TRUE(ty3.Matches(ValueDescr::Scalar(int64())));
249   ASSERT_FALSE(ty3.Matches(ValueDescr::Scalar(int32())));
250   ASSERT_FALSE(ty3.Matches(ValueDescr::Any(int64())));
251 }
252 
253 // ----------------------------------------------------------------------
254 // OutputType
255 
TEST(OutputType,Constructors)256 TEST(OutputType, Constructors) {
257   OutputType ty1 = int8();
258   ASSERT_EQ(OutputType::FIXED, ty1.kind());
259   AssertTypeEqual(*int8(), *ty1.type());
260 
261   auto DummyResolver = [](KernelContext*,
262                           const std::vector<ValueDescr>& args) -> Result<ValueDescr> {
263     return ValueDescr(int32(), GetBroadcastShape(args));
264   };
265   OutputType ty2(DummyResolver);
266   ASSERT_EQ(OutputType::COMPUTED, ty2.kind());
267 
268   ASSERT_OK_AND_ASSIGN(ValueDescr out_descr2, ty2.Resolve(nullptr, {}));
269   ASSERT_EQ(ValueDescr::Scalar(int32()), out_descr2);
270 
271   // Copy constructor
272   OutputType ty3 = ty1;
273   ASSERT_EQ(OutputType::FIXED, ty3.kind());
274   AssertTypeEqual(*ty1.type(), *ty3.type());
275 
276   OutputType ty4 = ty2;
277   ASSERT_EQ(OutputType::COMPUTED, ty4.kind());
278   ASSERT_OK_AND_ASSIGN(ValueDescr out_descr4, ty4.Resolve(nullptr, {}));
279   ASSERT_EQ(ValueDescr::Scalar(int32()), out_descr4);
280 
281   // Move constructor
282   OutputType ty5 = std::move(ty1);
283   ASSERT_EQ(OutputType::FIXED, ty5.kind());
284   AssertTypeEqual(*int8(), *ty5.type());
285 
286   OutputType ty6 = std::move(ty4);
287   ASSERT_EQ(OutputType::COMPUTED, ty6.kind());
288   ASSERT_OK_AND_ASSIGN(ValueDescr out_descr6, ty6.Resolve(nullptr, {}));
289   ASSERT_EQ(ValueDescr::Scalar(int32()), out_descr6);
290 
291   // ToString
292 
293   // ty1 was copied to ty3
294   ASSERT_EQ("int8", ty3.ToString());
295   ASSERT_EQ("computed", ty2.ToString());
296 }
297 
TEST(OutputType,Resolve)298 TEST(OutputType, Resolve) {
299   // Check shape promotion rules for FIXED kind
300   OutputType ty1(int32());
301 
302   ASSERT_OK_AND_ASSIGN(ValueDescr descr, ty1.Resolve(nullptr, {}));
303   ASSERT_EQ(ValueDescr::Scalar(int32()), descr);
304 
305   ASSERT_OK_AND_ASSIGN(descr,
306                        ty1.Resolve(nullptr, {ValueDescr(int8(), ValueDescr::SCALAR)}));
307   ASSERT_EQ(ValueDescr::Scalar(int32()), descr);
308 
309   ASSERT_OK_AND_ASSIGN(descr,
310                        ty1.Resolve(nullptr, {ValueDescr(int8(), ValueDescr::SCALAR),
311                                              ValueDescr(int8(), ValueDescr::ARRAY)}));
312   ASSERT_EQ(ValueDescr::Array(int32()), descr);
313 
314   OutputType ty2([](KernelContext*, const std::vector<ValueDescr>& args) {
315     return ValueDescr(args[0].type, GetBroadcastShape(args));
316   });
317 
318   ASSERT_OK_AND_ASSIGN(descr, ty2.Resolve(nullptr, {ValueDescr::Array(utf8())}));
319   ASSERT_EQ(ValueDescr::Array(utf8()), descr);
320 
321   // Type resolver that returns an error
322   OutputType ty3(
323       [](KernelContext* ctx, const std::vector<ValueDescr>& args) -> Result<ValueDescr> {
324         // NB: checking the value types versus the function arity should be
325         // validated elsewhere, so this is just for illustration purposes
326         if (args.size() == 0) {
327           return Status::Invalid("Need at least one argument");
328         }
329         return ValueDescr(args[0]);
330       });
331   ASSERT_RAISES(Invalid, ty3.Resolve(nullptr, {}));
332 
333   // Type resolver that returns ValueDescr::ANY and needs type promotion
334   OutputType ty4(
335       [](KernelContext* ctx, const std::vector<ValueDescr>& args) -> Result<ValueDescr> {
336         return int32();
337       });
338 
339   ASSERT_OK_AND_ASSIGN(descr, ty4.Resolve(nullptr, {ValueDescr::Array(int8())}));
340   ASSERT_EQ(ValueDescr::Array(int32()), descr);
341   ASSERT_OK_AND_ASSIGN(descr, ty4.Resolve(nullptr, {ValueDescr::Scalar(int8())}));
342   ASSERT_EQ(ValueDescr::Scalar(int32()), descr);
343 }
344 
TEST(OutputType,ResolveDescr)345 TEST(OutputType, ResolveDescr) {
346   ValueDescr d1 = ValueDescr::Scalar(int32());
347   ValueDescr d2 = ValueDescr::Array(int32());
348 
349   OutputType ty1(d1);
350   OutputType ty2(d2);
351 
352   ASSERT_EQ(ValueDescr::SCALAR, ty1.shape());
353   ASSERT_EQ(ValueDescr::ARRAY, ty2.shape());
354 
355   {
356     ASSERT_OK_AND_ASSIGN(ValueDescr descr, ty1.Resolve(nullptr, {}));
357     ASSERT_EQ(d1, descr);
358   }
359 
360   {
361     ASSERT_OK_AND_ASSIGN(ValueDescr descr, ty2.Resolve(nullptr, {}));
362     ASSERT_EQ(d2, descr);
363   }
364 }
365 
366 // ----------------------------------------------------------------------
367 // KernelSignature
368 
TEST(KernelSignature,Basics)369 TEST(KernelSignature, Basics) {
370   // (any[int8], scalar[decimal]) -> utf8
371   std::vector<InputType> in_types({int8(), InputType(Type::DECIMAL, ValueDescr::SCALAR)});
372   OutputType out_type(utf8());
373 
374   KernelSignature sig(in_types, out_type);
375   ASSERT_EQ(2, sig.in_types().size());
376   ASSERT_TRUE(sig.in_types()[0].type()->Equals(*int8()));
377   ASSERT_TRUE(sig.in_types()[0].Matches(ValueDescr::Scalar(int8())));
378   ASSERT_TRUE(sig.in_types()[0].Matches(ValueDescr::Array(int8())));
379 
380   ASSERT_TRUE(sig.in_types()[1].Matches(ValueDescr::Scalar(decimal(12, 2))));
381   ASSERT_FALSE(sig.in_types()[1].Matches(ValueDescr::Array(decimal(12, 2))));
382 }
383 
TEST(KernelSignature,Equals)384 TEST(KernelSignature, Equals) {
385   KernelSignature sig1({}, utf8());
386   KernelSignature sig1_copy({}, utf8());
387   KernelSignature sig2({int8()}, utf8());
388 
389   // Output type doesn't matter (for now)
390   KernelSignature sig3({int8()}, int32());
391 
392   KernelSignature sig4({int8(), int16()}, utf8());
393   KernelSignature sig4_copy({int8(), int16()}, utf8());
394   KernelSignature sig5({int8(), int16(), int32()}, utf8());
395 
396   // Differ in shape
397   KernelSignature sig6({ValueDescr::Scalar(int8())}, utf8());
398   KernelSignature sig7({ValueDescr::Array(int8())}, utf8());
399 
400   ASSERT_EQ(sig1, sig1);
401 
402   ASSERT_EQ(sig2, sig3);
403   ASSERT_NE(sig3, sig4);
404 
405   // Different sig objects, but same sig
406   ASSERT_EQ(sig1, sig1_copy);
407   ASSERT_EQ(sig4, sig4_copy);
408 
409   // Match first 2 args, but not third
410   ASSERT_NE(sig4, sig5);
411 
412   ASSERT_NE(sig6, sig7);
413 }
414 
TEST(KernelSignature,VarArgsEquals)415 TEST(KernelSignature, VarArgsEquals) {
416   KernelSignature sig1({int8()}, utf8(), /*is_varargs=*/true);
417   KernelSignature sig2({int8()}, utf8(), /*is_varargs=*/true);
418   KernelSignature sig3({int8()}, utf8());
419 
420   ASSERT_EQ(sig1, sig2);
421   ASSERT_NE(sig2, sig3);
422 }
423 
TEST(KernelSignature,Hash)424 TEST(KernelSignature, Hash) {
425   // Some basic tests to ensure that the hashes are deterministic and that all
426   // input arguments are incorporated
427   KernelSignature sig1({}, utf8());
428   KernelSignature sig2({int8()}, utf8());
429   KernelSignature sig3({int8(), int32()}, utf8());
430 
431   ASSERT_EQ(sig1.Hash(), sig1.Hash());
432   ASSERT_EQ(sig2.Hash(), sig2.Hash());
433   ASSERT_NE(sig1.Hash(), sig2.Hash());
434   ASSERT_NE(sig2.Hash(), sig3.Hash());
435 }
436 
TEST(KernelSignature,MatchesInputs)437 TEST(KernelSignature, MatchesInputs) {
438   // () -> boolean
439   KernelSignature sig1({}, boolean());
440 
441   ASSERT_TRUE(sig1.MatchesInputs({}));
442   ASSERT_FALSE(sig1.MatchesInputs({int8()}));
443 
444   // (any[int8], any[decimal]) -> boolean
445   KernelSignature sig2({int8(), InputType(Type::DECIMAL)}, boolean());
446 
447   ASSERT_FALSE(sig2.MatchesInputs({}));
448   ASSERT_FALSE(sig2.MatchesInputs({int8()}));
449   ASSERT_TRUE(sig2.MatchesInputs({int8(), decimal(12, 2)}));
450   ASSERT_TRUE(sig2.MatchesInputs(
451       {ValueDescr::Scalar(int8()), ValueDescr::Scalar(decimal(12, 2))}));
452   ASSERT_TRUE(
453       sig2.MatchesInputs({ValueDescr::Array(int8()), ValueDescr::Array(decimal(12, 2))}));
454 
455   // (scalar[int8], array[int32]) -> boolean
456   KernelSignature sig3({ValueDescr::Scalar(int8()), ValueDescr::Array(int32())},
457                        boolean());
458 
459   ASSERT_FALSE(sig3.MatchesInputs({}));
460 
461   // Unqualified, these are ANY type and do not match because the kernel
462   // requires a scalar and an array
463   ASSERT_FALSE(sig3.MatchesInputs({int8(), int32()}));
464   ASSERT_TRUE(
465       sig3.MatchesInputs({ValueDescr::Scalar(int8()), ValueDescr::Array(int32())}));
466   ASSERT_FALSE(
467       sig3.MatchesInputs({ValueDescr::Array(int8()), ValueDescr::Array(int32())}));
468 }
469 
TEST(KernelSignature,VarArgsMatchesInputs)470 TEST(KernelSignature, VarArgsMatchesInputs) {
471   {
472     KernelSignature sig({int8()}, utf8(), /*is_varargs=*/true);
473 
474     std::vector<ValueDescr> args = {int8()};
475     ASSERT_TRUE(sig.MatchesInputs(args));
476     args.push_back(ValueDescr::Scalar(int8()));
477     args.push_back(ValueDescr::Array(int8()));
478     ASSERT_TRUE(sig.MatchesInputs(args));
479     args.push_back(int32());
480     ASSERT_FALSE(sig.MatchesInputs(args));
481   }
482   {
483     KernelSignature sig({int8(), utf8()}, utf8(), /*is_varargs=*/true);
484 
485     std::vector<ValueDescr> args = {int8()};
486     ASSERT_TRUE(sig.MatchesInputs(args));
487     args.push_back(ValueDescr::Scalar(utf8()));
488     args.push_back(ValueDescr::Array(utf8()));
489     ASSERT_TRUE(sig.MatchesInputs(args));
490     args.push_back(int32());
491     ASSERT_FALSE(sig.MatchesInputs(args));
492   }
493 }
494 
TEST(KernelSignature,ToString)495 TEST(KernelSignature, ToString) {
496   std::vector<InputType> in_types = {InputType(int8(), ValueDescr::SCALAR),
497                                      InputType(Type::DECIMAL, ValueDescr::ARRAY),
498                                      InputType(utf8())};
499   KernelSignature sig(in_types, utf8());
500   ASSERT_EQ("(scalar[int8], array[Type::DECIMAL128], any[string]) -> string",
501             sig.ToString());
502 
503   OutputType out_type([](KernelContext*, const std::vector<ValueDescr>& args) {
504     return Status::Invalid("NYI");
505   });
506   KernelSignature sig2({int8(), InputType(Type::DECIMAL)}, out_type);
507   ASSERT_EQ("(any[int8], any[Type::DECIMAL128]) -> computed", sig2.ToString());
508 }
509 
TEST(KernelSignature,VarArgsToString)510 TEST(KernelSignature, VarArgsToString) {
511   KernelSignature sig({int8()}, utf8(), /*is_varargs=*/true);
512   ASSERT_EQ("varargs[any[int8]] -> string", sig.ToString());
513 }
514 
515 }  // namespace compute
516 }  // namespace arrow
517