@@ -1268,6 +1268,91 @@ defmodule Module.Types.DescrTest do
1268
1268
# assert difference(tuple([number(), term()]), tuple([integer(), atom()]))
1269
1269
# |> to_quoted_string() ==
1270
1270
# "{float(), term()} or {number(), term() and not atom()}"
1271
+
1272
+ assert union ( tuple ( [ integer ( ) , atom ( ) ] ) , tuple ( [ integer ( ) , atom ( ) ] ) ) |> to_quoted_string ( ) ==
1273
+ "{integer(), atom()}"
1274
+
1275
+ assert union ( tuple ( [ integer ( ) , atom ( ) ] ) , tuple ( [ float ( ) , atom ( ) ] ) ) |> to_quoted_string ( ) ==
1276
+ "{float() or integer(), atom()}"
1277
+
1278
+ assert union ( tuple ( [ integer ( ) , atom ( ) ] ) , tuple ( [ float ( ) , atom ( ) ] ) )
1279
+ |> union ( tuple ( [ pid ( ) , pid ( ) , port ( ) ] ) )
1280
+ |> union ( tuple ( [ pid ( ) , pid ( ) , atom ( ) ] ) )
1281
+ |> to_quoted_string ( ) ==
1282
+ "{float() or integer(), atom()} or {pid(), pid(), atom() or port()}"
1283
+
1284
+ assert union ( open_tuple ( [ integer ( ) ] ) , open_tuple ( [ float ( ) ] ) ) |> to_quoted_string ( ) ==
1285
+ "{float() or integer(), ...}"
1286
+
1287
+ # {:ok, {term(), integer()}} or {:ok, {term(), float()}} or {:exit, :kill} or {:exit, :timeout}
1288
+ assert tuple ( [ atom ( [ :ok ] ) , tuple ( [ term ( ) , empty_list ( ) ] ) ] )
1289
+ |> union ( tuple ( [ atom ( [ :ok ] ) , tuple ( [ term ( ) , open_map ( ) ] ) ] ) )
1290
+ |> union ( tuple ( [ atom ( [ :exit ] ) , atom ( [ :kill ] ) ] ) )
1291
+ |> union ( tuple ( [ atom ( [ :exit ] ) , atom ( [ :timeout ] ) ] ) )
1292
+ |> to_quoted_string ( ) ==
1293
+ "{:exit, :kill or :timeout} or {:ok, {term(), %{...} or empty_list()}}"
1294
+
1295
+ # Detection of duplicates
1296
+ assert tuple ( [ atom ( [ :ok ] ) , term ( ) ] )
1297
+ |> union ( tuple ( [ atom ( [ :ok ] ) , term ( ) ] ) )
1298
+ |> to_quoted_string ( ) == "{:ok, term()}"
1299
+
1300
+ assert tuple ( [ closed_map ( a: integer ( ) , b: atom ( ) ) , open_map ( ) ] )
1301
+ |> union ( tuple ( [ closed_map ( a: integer ( ) , b: atom ( ) ) , open_map ( ) ] ) )
1302
+ |> to_quoted_string ( ) ==
1303
+ "{%{a: integer(), b: atom()}, %{...}}"
1304
+
1305
+ # Nested fusion
1306
+ assert tuple ( [ closed_map ( a: integer ( ) , b: atom ( ) ) , open_map ( ) ] )
1307
+ |> union ( tuple ( [ closed_map ( a: float ( ) , b: atom ( ) ) , open_map ( ) ] ) )
1308
+ |> to_quoted_string ( ) ==
1309
+ "{%{a: float() or integer(), b: atom()}, %{...}}"
1310
+
1311
+ # Complex simplification of map/tuple combinations. Initial type is:
1312
+ # ```
1313
+ # dynamic(
1314
+ # :error or
1315
+ # ({%Decimal{coef: :inf, exp: integer(), sign: integer()}, binary()} or
1316
+ # {%Decimal{coef: :NaN, exp: integer(), sign: integer()}, binary()} or
1317
+ # {%Decimal{coef: integer(), exp: integer(), sign: integer()}, term()} or
1318
+ # {%Decimal{coef: :inf, exp: integer(), sign: integer()} or
1319
+ # %Decimal{coef: :NaN, exp: integer(), sign: integer()} or
1320
+ # %Decimal{coef: integer(), exp: integer(), sign: integer()}, term()})
1321
+ # )
1322
+ # ```
1323
+ decimal_inf =
1324
+ closed_map (
1325
+ __struct__: atom ( [ Decimal ] ) ,
1326
+ coef: atom ( [ :inf ] ) ,
1327
+ exp: integer ( ) ,
1328
+ sign: integer ( )
1329
+ )
1330
+
1331
+ decimal_nan =
1332
+ closed_map (
1333
+ __struct__: atom ( [ Decimal ] ) ,
1334
+ coef: atom ( [ :NaN ] ) ,
1335
+ exp: integer ( ) ,
1336
+ sign: integer ( )
1337
+ )
1338
+
1339
+ decimal_int =
1340
+ closed_map ( __struct__: atom ( [ Decimal ] ) , coef: integer ( ) , exp: integer ( ) , sign: integer ( ) )
1341
+
1342
+ assert atom ( [ :error ] )
1343
+ |> union (
1344
+ tuple ( [ decimal_inf , binary ( ) ] )
1345
+ |> union (
1346
+ tuple ( [ decimal_nan , binary ( ) ] )
1347
+ |> union (
1348
+ tuple ( [ decimal_int , term ( ) ] )
1349
+ |> union ( tuple ( [ union ( decimal_inf , union ( decimal_nan , decimal_int ) ) , term ( ) ] ) )
1350
+ )
1351
+ )
1352
+ )
1353
+ |> dynamic ( )
1354
+ |> to_quoted_string ( ) ==
1355
+ "dynamic(\n :error or\n ({%Decimal{coef: integer() or (:NaN or :inf), exp: integer(), sign: integer()}, term()} or\n {%Decimal{coef: :NaN or :inf, exp: integer(), sign: integer()}, binary()})\n )"
1271
1356
end
1272
1357
1273
1358
test "map" do
@@ -1311,6 +1396,50 @@ defmodule Module.Types.DescrTest do
1311
1396
assert difference ( open_map ( a: number ( ) , b: atom ( ) ) , open_map ( a: integer ( ) ) )
1312
1397
|> to_quoted_string ( ) == "%{..., a: float(), b: atom()}"
1313
1398
1399
+ # Basic map fusion
1400
+ assert union ( closed_map ( a: integer ( ) ) , closed_map ( a: integer ( ) ) ) |> to_quoted_string ( ) ==
1401
+ "%{a: integer()}"
1402
+
1403
+ assert union ( closed_map ( a: integer ( ) ) , closed_map ( a: float ( ) ) ) |> to_quoted_string ( ) ==
1404
+ "%{a: float() or integer()}"
1405
+
1406
+ # Nested fusion
1407
+ assert union ( closed_map ( a: integer ( ) , b: atom ( ) ) , closed_map ( a: float ( ) , b: atom ( ) ) )
1408
+ |> union ( closed_map ( x: pid ( ) , y: pid ( ) , z: port ( ) ) )
1409
+ |> union ( closed_map ( x: pid ( ) , y: pid ( ) , z: atom ( ) ) )
1410
+ |> to_quoted_string ( ) ==
1411
+ "%{a: float() or integer(), b: atom()} or %{x: pid(), y: pid(), z: atom() or port()}"
1412
+
1413
+ # Open map fusion
1414
+ assert union ( open_map ( a: integer ( ) ) , open_map ( a: float ( ) ) ) |> to_quoted_string ( ) ==
1415
+ "%{..., a: float() or integer()}"
1416
+
1417
+ # Fusing complex nested maps with unions
1418
+ assert closed_map ( status: atom ( [ :ok ] ) , data: closed_map ( value: term ( ) , count: empty_list ( ) ) )
1419
+ |> union (
1420
+ closed_map ( status: atom ( [ :ok ] ) , data: closed_map ( value: term ( ) , count: open_map ( ) ) )
1421
+ )
1422
+ |> union ( closed_map ( status: atom ( [ :error ] ) , reason: atom ( [ :timeout ] ) ) )
1423
+ |> union ( closed_map ( status: atom ( [ :error ] ) , reason: atom ( [ :crash ] ) ) )
1424
+ |> to_quoted_string ( ) ==
1425
+ "%{data: %{count: %{...} or empty_list(), value: term()}, status: :ok} or\n %{reason: :crash or :timeout, status: :error}"
1426
+
1427
+ # Difference and union tests
1428
+ assert closed_map ( status: atom ( [ :ok ] ) , value: term ( ) )
1429
+ |> difference ( closed_map ( status: atom ( [ :ok ] ) , value: float ( ) ) )
1430
+ |> union (
1431
+ closed_map ( status: atom ( [ :ok ] ) , value: term ( ) )
1432
+ |> difference ( closed_map ( status: atom ( [ :ok ] ) , value: integer ( ) ) )
1433
+ )
1434
+ |> to_quoted_string ( ) ==
1435
+ "%{status: :ok, value: term()}"
1436
+
1437
+ # Nested map fusion
1438
+ assert closed_map ( data: closed_map ( x: integer ( ) , y: atom ( ) ) , meta: open_map ( ) )
1439
+ |> union ( closed_map ( data: closed_map ( x: float ( ) , y: atom ( ) ) , meta: open_map ( ) ) )
1440
+ |> to_quoted_string ( ) ==
1441
+ "%{data: %{x: float() or integer(), y: atom()}, meta: %{...}}"
1442
+
1314
1443
# Test complex combinations
1315
1444
assert intersection ( open_map ( a: number ( ) , b: atom ( ) ) , open_map ( a: integer ( ) , c: boolean ( ) ) )
1316
1445
|> union ( difference ( open_map ( x: atom ( ) ) , open_map ( x: boolean ( ) ) ) )
0 commit comments