using Compat
using Dates
using TOML
using Test

@test isempty(detect_ambiguities(Base, Core, Compat))

begin
    # A custom linear slow sparse-like array that relies upon Dict for its storage
    struct TSlow{T,N} <: AbstractArray{T,N}
        data::Dict{NTuple{N,Int}, T}
        dims::NTuple{N,Int}
    end
    TSlow(::Type{T}, dims::Int...) where {T} = TSlow(T, dims)
    TSlow(::Type{T}, dims::NTuple{N,Int}) where {T,N} = TSlow{T,N}(Dict{NTuple{N,Int}, T}(), dims)

    TSlow{T,N}(X::TSlow{T,N})         where {T,N  } = X
    TSlow(     X::AbstractArray{T,N}) where {T,N  } = TSlow{T,N}(X)
    TSlow{T  }(X::AbstractArray{_,N}) where {T,N,_} = TSlow{T,N}(X)
    TSlow{T,N}(X::AbstractArray     ) where {T,N  } = begin
        A = TSlow(T, size(X))
        for I in CartesianIndices(X)
            A[Tuple(I)...] = X[Tuple(I)...]
        end
        A
    end
    Base.size(A::TSlow) = A.dims
    Base.similar(A::TSlow, ::Type{T}, dims::Dims) where {T} = TSlow(T, dims)
    Base.IndexStyle(::Type{A}) where {A<:TSlow} = IndexCartesian()
    Base.getindex(A::TSlow{T,N}, i::Vararg{Int,N}) where {T,N} = get(A.data, i, zero(T))
    Base.setindex!(A::TSlow{T,N}, v, i::Vararg{Int,N}) where {T,N} = (A.data[i] = v)
end

# https://github.com/JuliaLang/julia/pull/35316
# https://github.com/JuliaLang/julia/pull/41076
@testset "2arg" begin
    @testset "findmin(f, domain)" begin
        @test findmin(-, 1:10) == (-10, 10)
        @test findmin(identity, [1, 2, 3, missing]) === (missing, 4)
        @test findmin(identity, [1, NaN, 3, missing]) === (missing, 4)
        @test findmin(identity, [1, missing, NaN, 3]) === (missing, 2)
        @test findmin(identity, [1, NaN, 3]) === (NaN, 2)
        @test findmin(identity, [1, 3, NaN]) === (NaN, 3)
        @test all(findmin(cos, 0:π/2:2π) .≈ (-1.0, 3))
    end

    @testset "findmax(f, domain)" begin
        @test findmax(-, 1:10) == (-1, 1)
        @test findmax(identity, [1, 2, 3, missing]) === (missing, 4)
        @test findmax(identity, [1, NaN, 3, missing]) === (missing, 4)
        @test findmax(identity, [1, missing, NaN, 3]) === (missing, 2)
        @test findmax(identity, [1, NaN, 3]) === (NaN, 2)
        @test findmax(identity, [1, 3, NaN]) === (NaN, 3)
        @test findmax(cos, 0:π/2:2π) == (1.0, 1)
    end

    @testset "argmin(f, domain)" begin
        @test argmin(-, 1:10) == 10
        @test argmin(sum, Iterators.product(1:5, 1:5)) == (1, 1)
    end

    @testset "argmax(f, domain)" begin
        @test argmax(-, 1:10) == 1
        @test argmax(sum, Iterators.product(1:5, 1:5)) == (5, 5)
    end
end

# https://github.com/JuliaLang/julia/pull/40729
@testset "@something" begin
    @test_throws ArgumentError @something()
    @test_throws ArgumentError @something(nothing)
    @test @something(1) === 1
    @test @something(Some(nothing)) === nothing

    @test @something(1, error("failed")) === 1
    @test_throws ErrorException @something(nothing, error("failed"))
end

@testset "@coalesce" begin
    @test @coalesce() === missing
    @test @coalesce(1) === 1
    @test @coalesce(nothing) === nothing
    @test @coalesce(missing) === missing

    @test @coalesce(1, error("failed")) === 1
    @test_throws ErrorException @coalesce(missing, error("failed"))
end

@testset "get" begin
     A = reshape([1:24...], 4, 3, 2)
     B = reshape([1:24...], 4, 3, 2)

     global c = 0
     f() = (global c = c+1; 0)
     @test get(f, A, ()) == 0
     @test c == 1
     @test get(f, B, ()) == 0
     @test c == 2
     @test get(f, A, (1,)) == get(f, A, 1) == A[1] == 1
     @test c == 2
     @test get(f, B, (1,)) == get(f, B, 1) == B[1] == 1
     @test c == 2
     @test get(f, A, (25,)) == get(f, A, 25) == 0
     @test c == 4
     @test get(f, B, (25,)) == get(f, B, 25) == 0
     @test c == 6
     @test get(f, A, (1,1,1)) == A[1,1,1] == 1
     @test get(f, B, (1,1,1)) == B[1,1,1] == 1
     @test get(f, A, (1,1,3)) == 0
     @test c == 7
     @test get(f, B, (1,1,3)) == 0
     @test c == 8
     @test get(f, TSlow([]), ()) == 0
     @test c == 9

     @test get((5, 6, 7), 1, 0) == 5
     @test get((), 5, 0) == 0
     @test get((1,), 3, 0) == 0
     @test get(()->0, (5, 6, 7), 1) == 5
     @test get(()->0, (), 4) == 0
     @test get(()->0, (1,), 3) == 0

    for x in [1.23, 7, ℯ, 4//5] #[FP, Int, Irrational, Rat]
         @test get(x, 1, 99) == x
         @test get(x, (), 99) == x
         @test get(x, (1,), 99) == x
         @test get(x, 2, 99) == 99
         @test get(x, 0, pi) == pi
         @test get(x, (1,2), pi) == pi
         c = Ref(0)
         @test get(() -> c[]+=1, x, 1) == x
         @test get(() -> c[]+=1, x, ()) == x
         @test get(() -> c[]+=1, x, (1,1,1)) == x
         @test get(() -> c[]+=1, x, 2) == 1
         @test get(() -> c[]+=1, x, -1) == 2
         @test get(() -> c[]+=1, x, (3,2,1)) == 3
    end
end

# https://github.com/JuliaLang/julia/pull/39285
struct X
    x
end
@testset "property destructuring assignment" begin
    nt = (; a=1, b=2, c=3)
    @compat (; c, b) = nt
    @test c == nt.c
    @test b == nt.b

    @compat (; x) = X(1)
    @test x == 1
end

# https://github.com/JuliaLang/julia/pull/29901
@testset "current_exceptions" begin
    # Helper method to retrieve an ExceptionStack that should contain two exceptions,
    # each of which accompanied by a backtrace or `nothing` according to `with_backtraces`.
    function _retrieve_exception_stack(;with_backtraces::Bool)
        exception_stack = try
            try
                # Generate the first exception:
                __not_a_binding__
            catch
                # Catch the first exception, and generate a second exception
                # during what would be handling of the first exception:
                1 ÷ 0
            end
        catch
            # Retrieve an ExceptionStack with both exceptions,
            # and bind `exception_stack` (at the top of this block) thereto:
            current_exceptions(;backtrace=with_backtraces)
        end
        return exception_stack
    end

    excs_with_bts = _retrieve_exception_stack(with_backtraces = true)
    excs_sans_bts = _retrieve_exception_stack(with_backtraces = false)

    # Check that the ExceptionStack with backtraces contains backtraces:
    BACKTRACE_TYPE = Vector{Union{Ptr{Nothing}, Base.InterpreterIP}}
    @test all(exc_with_bt[2] isa BACKTRACE_TYPE for exc_with_bt in excs_with_bts)

    # Check that the ExceptionStack without backtraces contains `nothing`s:
    @test all(exc_sans_bt[2] isa Nothing for exc_sans_bt in excs_sans_bts)

    # Check that the ExceptionStacks contain the expected exception types:
    @test typeof.(first.(excs_with_bts)) == [UndefVarError, DivideError]
    @test typeof.(first.(excs_sans_bts)) == [UndefVarError, DivideError]

    # Check that the ExceptionStack with backtraces `show`s correctly:
    @test occursin(r"""
    2-element ExceptionStack:
    DivideError: integer division error
    Stacktrace:.*

    caused by: UndefVarError: `?__not_a_binding__`? not defined( in `Main`)?
    (Suggestion: check for spelling errors or missing imports.
    )?Stacktrace:.*
    """s, sprint(show, excs_with_bts))

    # Check that the ExceptionStack without backtraces `show`s correctly:
    @test occursin(r"""
    2-element ExceptionStack:
    DivideError: integer division error

    caused by: UndefVarError: `?__not_a_binding__`? not defined"""s,
    sprint(show, excs_sans_bts))

    # Check that the ExceptionStack with backtraces `display_error`s correctly:
    @test occursin(r"""
    ERROR: DivideError: integer division error
    Stacktrace:.*

    caused by: UndefVarError: `?__not_a_binding__`? not defined( in `Main`)?
    (Suggestion: check for spelling errors or missing imports.
    )?Stacktrace:.*
    """s, sprint(Base.display_error, excs_with_bts))

    # Check that the ExceptionStack without backtraces `display_error`s correctly:
    @test occursin(r"""
    ERROR: DivideError: integer division error

    caused by: UndefVarError: `?__not_a_binding__`? not defined"""s,
    sprint(Base.display_error, excs_sans_bts))
end

# https://github.com/JuliaLang/julia/pull/39794
@testset "Returns" begin
    @test @inferred(Returns(1)()   ) === 1
    @test @inferred(Returns(1)(23) ) === 1
    @test @inferred(Returns("a")(2,3)) == "a"
    @test @inferred(Returns(1)(x=1, y=2)) === 1
    @test @inferred(Returns(Int)()) === Int
    @test @inferred(Returns(Returns(1))()) === Returns(1)
    f = @inferred Returns(Int)
    @inferred f(1,2)
    val = [1,2,3]
    @test Returns(val)(1) === val
    @test sprint(show, Returns(1.0)) == "Returns{Float64}(1.0)"
end

# https://github.com/JuliaLang/julia/pull/43852
@testset "@assume_effects" begin
    # ensure proper macro hygiene across versions
    Compat.@assume_effects :total foo() = true
    Compat.@assume_effects bar() = true
    @test foo()
    @test bar()
end

# https://github.com/JuliaLang/julia/pull/42125
@testset "@constprop" begin
    Compat.@constprop :aggressive aggf(x) = Symbol(x)
    Compat.@constprop :none      nonef(x) = Symbol(x)
    @test_throws Exception Meta.lower(@__MODULE__,
        quote
            Compat.@constprop :other brokenf(x) = Symbol(x)
        end
    )
    @test aggf("hi") == nonef("hi") == :hi
end

# https://github.com/JuliaLang/julia/pull/41312
@testset "`@inline`/`@noinline` annotations within a function body" begin
    callf(f, args...) = f(args...)
    function foo1(a)
        Compat.@inline
        sum(sincos(a))
    end
    foo2(a) = (Compat.@inline; sum(sincos(a)))
    foo3(a) = callf(a) do a
        Compat.@inline
        sum(sincos(a))
    end
    function foo4(a)
        Compat.@noinline
        sum(sincos(a))
    end
    foo5(a) = (Compat.@noinline; sum(sincos(a)))
    foo6(a) = callf(a) do a
        Compat.@noinline
        sum(sincos(a))
    end

    @test foo1(42) == foo2(42) == foo3(42) == foo4(42) == foo5(42) == foo6(42)
end

# https://github.com/JuliaLang/julia/pull/41328
@testset "callsite annotations of inlining" begin
    function foo1(a)
        Compat.@inline begin
            return sum(sincos(a))
        end
    end
    function foo2(a)
        Compat.@noinline begin
            return sum(sincos(a))
        end
    end

    @test foo1(42) == foo2(42)
end

# https://github.com/JuliaLang/julia/pull/40803
@testset "Convert CompoundPeriod to Period" begin
    @test convert(Month, Year(1) + Month(1)) === Month(13)
    @test convert(Second, Minute(1) + Second(30)) === Second(90)
    @test convert(Minute, Minute(1) + Second(60)) === Minute(2)
    @test convert(Millisecond, Minute(1) + Second(30)) === Millisecond(90_000)
    @test_throws InexactError convert(Minute, Minute(1) + Second(30))
    @test_throws MethodError convert(Month, Minute(1) + Second(30))
    @test_throws MethodError convert(Second, Month(1) + Second(30))
    @test_throws MethodError convert(Period, Minute(1) + Second(30))
    @test_throws MethodError convert(Dates.FixedPeriod, Minute(1) + Second(30))
end

@testset "ismutabletype" begin
    @test ismutabletype(Array)
    @test !ismutabletype(Tuple)
end

# https://github.com/JuliaLang/julia/pull/39245

#=
cmcaine commented on Sep 8, 2021

This PR implements split with eachsplit and uses eachsplit in a few other places in Base,
so it's kind of already covered by the existing tests.
Not sure it needs any more?

so, these are the Base.split tests, but replacing split with eachsplit |> collect
=#
@testset "eachsplit" begin
    @test eachsplit("foo,bar,baz", 'x') |> collect == ["foo,bar,baz"]
    @test eachsplit("foo,bar,baz", ',') |> collect == ["foo","bar","baz"]
    @test eachsplit("foo,bar,baz", ",") |> collect == ["foo","bar","baz"]
    @test eachsplit("foo,bar,baz", r",") |> collect == ["foo","bar","baz"]
    @test eachsplit("foo,bar,baz", ','; limit=0) |> collect == ["foo","bar","baz"]
    @test eachsplit("foo,bar,baz", ','; limit=1) |> collect == ["foo,bar,baz"]
    @test eachsplit("foo,bar,baz", ','; limit=2) |> collect == ["foo","bar,baz"]
    @test eachsplit("foo,bar,baz", ','; limit=3) |> collect == ["foo","bar","baz"]
    @test eachsplit("foo,bar", "o,b") |> collect == ["fo","ar"]

    @test eachsplit("", ',') |> collect == [""]
    @test eachsplit(",", ',') |> collect == ["",""]
    @test eachsplit(",,", ',') |> collect == ["","",""]
    @test eachsplit("", ','  ; keepempty=false) |> collect == SubString[]
    @test eachsplit(",", ',' ; keepempty=false) |> collect == SubString[]
    @test eachsplit(",,", ','; keepempty=false) |> collect == SubString[]

    @test eachsplit("a b c") |> collect == ["a","b","c"]
    @test eachsplit("a  b \t c\n") |> collect == ["a","b","c"]
    @test eachsplit("α  β \u2009 γ\n") |> collect == ["α","β","γ"]

    @test eachsplit("a b c"; limit=2) |> collect == ["a","b c"]
    @test eachsplit("a  b \t c\n"; limit=3) |> collect == ["a","b","\t c\n"]
    @test eachsplit("a b c"; keepempty=true) |> collect == ["a","b","c"]
    @test eachsplit("a  b \t c\n"; keepempty=true) |> collect == ["a","","b","","","c",""]

    let str = "a.:.ba..:..cba.:.:.dcba.:."
        @test eachsplit(str, ".:.") |> collect == ["a","ba.",".cba",":.dcba",""]
        @test eachsplit(str, ".:."; keepempty=false) |> collect == ["a","ba.",".cba",":.dcba"]
        @test eachsplit(str, ".:.") |> collect == ["a","ba.",".cba",":.dcba",""]
        @test eachsplit(str, r"\.(:\.)+") |> collect == ["a","ba.",".cba","dcba",""]
        @test eachsplit(str, r"\.(:\.)+"; keepempty=false) |> collect == ["a","ba.",".cba","dcba"]
        @test eachsplit(str, r"\.+:\.+") |> collect == ["a","ba","cba",":.dcba",""]
        @test eachsplit(str, r"\.+:\.+"; keepempty=false) |> collect == ["a","ba","cba",":.dcba"]
    end

    # zero-width splits
    @test eachsplit("", "") |> collect == rsplit("", "") == [""]
    @test eachsplit("abc", "") |> collect == rsplit("abc", "") == ["a","b","c"]
    @test eachsplit("abc", "", limit=2)  |> collect == ["a","bc"]

    @test eachsplit("", r"")  |> collect == [""]
    @test eachsplit("abc", r"") |> collect == ["a","b","c"]
    @test eachsplit("abcd", r"b?") |> collect == ["a","c","d"]
    @test eachsplit("abcd", r"b*") |> collect == ["a","c","d"]
    @test eachsplit("abcd", r"b+") |> collect == ["a","cd"]
    @test eachsplit("abcd", r"b?c?") |> collect == ["a","d"]
    @test eachsplit("abcd", r"[bc]?") |> collect == ["a","","d"]
    @test eachsplit("abcd", r"a*") |> collect == ["","b","c","d"]
    @test eachsplit("abcd", r"a+") |> collect == ["","bcd"]
    @test eachsplit("abcd", r"d*") |> collect == ["a","b","c",""]
    @test eachsplit("abcd", r"d+") |> collect == ["abc",""]
    @test eachsplit("abcd", r"[ad]?") |> collect == ["","b","c",""]

    # multi-byte unicode characters (issue #26225)
    @test eachsplit("α β γ", " ") |> collect == rsplit("α β γ", " ") ==
        eachsplit("α β γ", isspace) |> collect == rsplit("α β γ", isspace) == ["α","β","γ"]
    @test eachsplit("ö.", ".") |> collect == rsplit("ö.", ".") == ["ö",""]
    @test eachsplit("α β γ", "β") |> collect == rsplit("α β γ", "β") == ["α "," γ"]
end

# https://github.com/JuliaLang/julia/pull/43354
@testset "allequal" begin
    @test allequal(Set())
    @test allequal(Set(1))
    @test !allequal(Set([1, 2]))
    @test allequal(Dict())
    @test allequal(Dict(:a => 1))
    @test !allequal(Dict(:a => 1, :b => 2))
    @test allequal([])
    @test allequal([1])
    @test allequal([1, 1])
    @test !allequal([1, 1, 2])
    @test allequal([:a, :a])
    @test !allequal([:a, :b])
    @test !allequal(1:2)
    @test allequal(1:1)
    @test !allequal(4.0:0.3:7.0)
    @test allequal(4:-1:5)       # empty range
    @test !allequal(7:-1:1)       # negative step
    @test !allequal(Date(2018, 8, 7):Day(1):Date(2018, 8, 11))  # JuliaCon 2018
    @test !allequal(DateTime(2018, 8, 7):Hour(1):DateTime(2018, 8, 11))
    @test allequal(StepRangeLen(1.0, 0.0, 2))
    @test !allequal(StepRangeLen(1.0, 1.0, 2))
    @test allequal(LinRange(1, 1, 0))
    @test allequal(LinRange(1, 1, 1))
    @test allequal(LinRange(1, 1, 2))
    @test !allequal(LinRange(1, 2, 2))
end

@testset "keepat!" begin
    a = [1:6;]
    @test a === keepat!(a, 1:5)
    @test a == 1:5
    @test keepat!(a, [2, 4]) == [2, 4]
    @test isempty(keepat!(a, []))

    a = [1:6;]
    @test_throws BoundsError keepat!(a, 1:10) # make sure this is not a no-op
    @test_throws BoundsError keepat!(a, 2:10)
    @test_throws ArgumentError keepat!(a, [2, 4, 3])

    b = BitVector([1, 1, 1, 0, 0])
    @test b === keepat!(b, 1:5)
    @test b == [1, 1, 1, 0, 0]
    @test keepat!(b, 2:4) == [1, 1, 0]
    @test_throws BoundsError keepat!(a, -1:10)
    @test_throws ArgumentError keepat!(a, [2, 1])
    @test isempty(keepat!(a, []))

    # Vector
    a = Vector(1:10)
    keepat!(a, [falses(5); trues(5)])
    @test a == 6:10
    @test_throws BoundsError keepat!(a, trues(1))
    @test_throws BoundsError keepat!(a, trues(11))

    # BitVector
    ba = rand(10) .> 0.5
    @test isa(ba, BitArray)
    keepat!(ba, ba)
    @test all(ba)

    # empty array
    ea = []
    keepat!(ea, Bool[])
    @test isempty(ea)
end

@testset "pkgversion" begin
    toml = joinpath(pkgdir(Compat), "Project.toml")
    @test pkgversion(Compat) == VersionNumber(TOML.parsefile(toml)["version"])
    @test pkgversion(Base) === nothing
end

# https://github.com/JuliaLang/julia/pull/43334
@testset "stack" begin
    # Basics
    for args in ([[1, 2]], [1:2, 3:4], [[1 2; 3 4], [5 6; 7 8]],
                AbstractVector[1:2, [3.5, 4.5]], Vector[[1,2], [3im, 4im]],
                [[1:2, 3:4], [5:6, 7:8]], [fill(1), fill(2)])
        X = stack(args)
        Y = cat(args...; dims=ndims(args[1])+1)
        @test X == Y
        @test typeof(X) === typeof(Y)

        X2 = stack(x for x in args)
        @test X2 == Y
        @test typeof(X2) === typeof(Y)

        X3 = stack(x for x in args if true)
        @test X3 == Y
        @test typeof(X3) === typeof(Y)

        if isconcretetype(eltype(args))
            @inferred stack(args)
            @inferred stack(x for x in args)
        end
    end

    # Higher dims
    @test size(stack([rand(2,3) for _ in 1:4, _ in 1:5])) == (2,3,4,5)
    @test size(stack(rand(2,3) for _ in 1:4, _ in 1:5)) == (2,3,4,5)
    @test size(stack(rand(2,3) for _ in 1:4, _ in 1:5 if true)) == (2, 3, 20)
    @test size(stack([rand(2,3) for _ in 1:4, _ in 1:5]; dims=1)) == (20, 2, 3)
    @test size(stack(rand(2,3) for _ in 1:4, _ in 1:5; dims=2)) == (2, 20, 3)

    # Tuples
    @test stack([(1,2), (3,4)]) == [1 3; 2 4]
    @test stack(((1,2), (3,4))) == [1 3; 2 4]
    @test stack(Any[(1,2), (3,4)]) == [1 3; 2 4]
    @test stack([(1,2), (3,4)]; dims=1) == [1 2; 3 4]
    @test stack(((1,2), (3,4)); dims=1) == [1 2; 3 4]
    @test stack(Any[(1,2), (3,4)]; dims=1) == [1 2; 3 4]
    @test size(@inferred stack(Iterators.product(1:3, 1:4))) == (2,3,4)
    @test @inferred(stack([('a', 'b'), ('c', 'd')])) == ['a' 'c'; 'b' 'd']
    @test @inferred(stack([(1,2+3im), (4, 5+6im)])) isa Matrix{Number}

    # stack(f, iter)
    @test @inferred(stack(x -> [x, 2x], 3:5)) == [3 4 5; 6 8 10]
    @test @inferred(stack(x -> x*x'/2, [1:2, 3:4])) == reshape([0.5, 1.0, 1.0, 2.0, 4.5, 6.0, 6.0, 8.0], 2, 2, 2)
    @test @inferred(stack(*, [1:2, 3:4], 5:6)) == [5 18; 10 24]

    # Iterators
    @test stack([(a=1,b=2), (a=3,b=4)]) == [1 3; 2 4]
    @test stack([(a=1,b=2), (c=3,d=4)]) == [1 3; 2 4]
    @test stack([(a=1,b=2), (c=3,d=4)]; dims=1) == [1 2; 3 4]
    @test stack([(a=1,b=2), (c=3,d=4)]; dims=2) == [1 3; 2 4]
    @test stack((x/y for x in 1:3) for y in 4:5) == (1:3) ./ (4:5)'
    @test stack((x/y for x in 1:3) for y in 4:5; dims=1) == (1:3)' ./ (4:5)

    # Exotic
    ips = ((Iterators.product([i,i^2], [2i,3i,4i], 1:4)) for i in 1:5)
    @test size(stack(ips)) == (2, 3, 4, 5)
    @test stack(ips) == cat(collect.(ips)...; dims=4)
    ips_cat2 = cat(reshape.(collect.(ips), Ref((2,1,3,4)))...; dims=2)
    @test stack(ips; dims=2) == ips_cat2
    @test stack(collect.(ips); dims=2) == ips_cat2
    ips_cat3 = cat(reshape.(collect.(ips), Ref((2,3,1,4)))...; dims=3)
    @test stack(ips; dims=3) == ips_cat3  # path for non-array accumulation on non-final dims
    @test stack(collect, ips; dims=3) == ips_cat3  # ... and for array accumulation
    @test stack(collect.(ips); dims=3) == ips_cat3

    # Trivial, because numbers are iterable:
    @test stack(abs2, 1:3) == [1, 4, 9] == collect(Iterators.flatten(abs2(x) for x in 1:3))

    # Allocation tests
    xv = [rand(10) for _ in 1:100]
    xt = Tuple.(xv)
    for dims in (1, 2, :)
        @test stack(xv; dims) == stack(xt; dims)
        @test_skip 9000 > @allocated stack(xv; dims)
        @test_skip 9000 > @allocated stack(xt; dims)
    end
    xr = (reshape(1:1000,10,10,10) for _ = 1:1000)
    for dims in (1, 2, 3, :)
        stack(xr; dims)
        @test_skip 8.1e6 > @allocated stack(xr; dims)
    end

    # Mismatched sizes
    @test_throws DimensionMismatch stack([1:2, 1:3])
    @test_throws DimensionMismatch stack([1:2, 1:3]; dims=1)
    @test_throws DimensionMismatch stack([1:2, 1:3]; dims=2)
    @test_throws DimensionMismatch stack([(1,2), (3,4,5)])
    @test_throws DimensionMismatch stack([(1,2), (3,4,5)]; dims=1)
    @test_throws DimensionMismatch stack(x for x in [1:2, 1:3])
    @test_throws DimensionMismatch stack([[5 6; 7 8], [1, 2, 3, 4]])
    @test_throws DimensionMismatch stack([[5 6; 7 8], [1, 2, 3, 4]]; dims=1)
    @test_throws DimensionMismatch stack(x for x in [[5 6; 7 8], [1, 2, 3, 4]])
    # Inner iterator of unknown length
    @test_throws MethodError stack((x for x in 1:3 if true) for _ in 1:4)
    @test_throws MethodError stack((x for x in 1:3 if true) for _ in 1:4; dims=1)

    @test_throws ArgumentError stack([1:3, 4:6]; dims=0)
    @test_throws ArgumentError stack([1:3, 4:6]; dims=3)
    @test_throws ArgumentError stack(abs2, 1:3; dims=2)

    # Empty
    @test_throws ArgumentError stack(())
    @test_throws ArgumentError stack([])
    @test_throws ArgumentError stack(x for x in 1:3 if false)
end

@testset "promoting ops for TimePeriod" begin
    x = 10
    y = 3
    xs = Second(x)
    ys = Second(y)
    xms = Millisecond(xs)
    yms = Millisecond(ys)
    for op in (/, div, rem, mod, lcm, gcd)
        @test op(xms, yms) == op(xms, ys) == op(xs, yms)
    end
end

@testset "splat" begin
    @test splat(+)((1,2,3)) == 6

    if v"1.10.0-" <= VERSION < v"1.10.0-DEV.360" ||
        v"1.9.0-DEV.513" <= VERSION < v"1.9.0-beta3"
        # these versions of Base export Splat (which we use) but pretty-print with capital `S`
        @test repr(splat(+)) == "Splat(+)"
        @test repr(MIME"text/plain"(), splat(+)) == "Splat(+)"
    else
        @test repr(splat(+)) == "splat(+)"
        @test repr(MIME"text/plain"(), splat(+)) == "splat(+)"
    end
end

# https://github.com/JuliaLang/julia/pull/25085
@testset "Bool rounding (#25074)" begin
    @testset "round Bool" begin
        @test_throws InexactError round(Bool, -4.1)
        @test_throws InexactError round(Bool, 1.5)
        @test true == round(Bool, 1.0)
        @test false == round(Bool, 0.0)
        @test true == round(Bool, 0.6)
        @test false == round(Bool, 0.4)
        @test false == round(Bool, 0.5)
        @test false == round(Bool, -0.5)
    end

    @testset "trunc Bool" begin
        @test_throws InexactError trunc(Bool, -4.1)
        @test_throws InexactError trunc(Bool, 2.5)
        @test true == trunc(Bool, 1.0)
        @test false == trunc(Bool, 0.0)
        @test false == trunc(Bool, 0.6)
        @test false == trunc(Bool, 0.4)
        @test true == trunc(Bool, 1.8)
        @test false == trunc(Bool, -0.5)
    end

    @testset "floor Bool" begin
        @test_throws InexactError floor(Bool, -0.1)
        @test_throws InexactError floor(Bool, 2.5)
        @test true == floor(Bool, 1.0)
        @test false == floor(Bool, 0.0)
        @test false == floor(Bool, 0.6)
        @test true == floor(Bool, 1.8)
    end

    @testset "ceil Bool" begin
        @test_throws InexactError ceil(Bool, -1.4)
        @test_throws InexactError ceil(Bool, 1.5)
        @test true == ceil(Bool, 1.0)
        @test false == ceil(Bool, 0.0)
        @test true == ceil(Bool, 0.6)
        @test false == ceil(Bool, -0.7)
    end
end

# https://github.com/JuliaLang/julia/pull/37978
@testset "redirect_stdio" begin
    randstring() = join(rand('a':'z', 10))

    function hello_err_out()
        println(stderr, "hello from stderr")
        println(stdout, "hello from stdout")
    end
    @testset "same path for multiple streams" begin
        @test_throws ArgumentError redirect_stdio(hello_err_out,
                                            stdin="samepath.txt", stdout="samepath.txt")
        @test_throws ArgumentError redirect_stdio(hello_err_out,
                                            stdin="samepath.txt", stderr="samepath.txt")

        @test_throws ArgumentError redirect_stdio(hello_err_out,
                                            stdin=joinpath("tricky", "..", "samepath.txt"),
                                            stderr="samepath.txt")
        mktempdir() do dir
            path = joinpath(dir, "stdouterr.txt")
            redirect_stdio(hello_err_out, stdout=path, stderr=path)
            @test read(path, String) == """
            hello from stderr
            hello from stdout
            """
        end
    end

    mktempdir() do dir
        path_stdout = joinpath(dir, "stdout.txt")
        path_stderr = joinpath(dir, "stderr.txt")
        redirect_stdio(hello_err_out, stderr=devnull, stdout=path_stdout)
        @test read(path_stdout, String) == "hello from stdout\n"

        open(path_stderr, "w") do ioerr
            redirect_stdio(hello_err_out, stderr=ioerr, stdout=devnull)
        end
        @test read(path_stderr, String) == "hello from stderr\n"
    end

    mktempdir() do dir
        path_stderr = joinpath(dir, "stderr.txt")
        path_stdin  = joinpath(dir, "stdin.txt")
        path_stdout = joinpath(dir, "stdout.txt")

        content_stderr = randstring()
        content_stdout = randstring()

        redirect_stdio(stdout=path_stdout, stderr=path_stderr) do
            print(content_stdout)
            print(stderr, content_stderr)
        end

        @test read(path_stderr, String) == content_stderr
        @test read(path_stdout, String) == content_stdout
    end
end

module Mod50105
    using Compat
    @compat public foo, var"#", baz
    @compat public @mac1
    @compat public f00, @mac2, @mac3
    @compat public @mac4, @mac5
end

# https://github.com/JuliaLang/julia/pull/50105
@testset "@compat public" begin
    @compat public foo_50105
    # foo_50105 = 4 # Uncommenting this line would cause errors due to https://github.com/JuliaLang/julia/issues/51325
    @test Base.isexported(@__MODULE__, :foo_50105) === false
    VERSION >= v"1.11.0-DEV.469" && @test Base.ispublic(@__MODULE__, :foo_50105)
    for sym in [:foo, Symbol("#"), :baz, Symbol("@mac1"), :f00, Symbol("@mac2"), Symbol("@mac3"), Symbol("@mac4"), Symbol("@mac5")]
        @test Base.isexported(Mod50105, sym) === false
        VERSION >= v"1.11.0-DEV.469" && @test Base.ispublic(Mod50105, sym)
    end

    @test_throws LoadError @eval @compat public 4, bar
    @test_throws LoadError @eval @compat public foo bar
    @test_throws LoadError @eval @compat publac foo, bar
    @test_throws LoadError @eval @compat public 4, @bar
    @test_throws LoadError @eval @compat public foo @bar
    @test_throws LoadError @eval @compat publac foo, @bar
    @test_throws LoadError @eval @compat public @bar, 4
    @test_throws LoadError @eval @compat public @bar foo
    @test_throws LoadError @eval @compat publac @bar, foo
end

# https://github.com/JuliaLang/julia/pull/47679
@testset "allunique(f, xs)" begin
    @test allunique(sin, 1:3)
    @test !allunique(sin, [1,2,3,1])
    @test allunique(sin, (1, 2, pi, im))  # eltype Any
    @test allunique(abs2, 1:100)
    @test !allunique(abs, -10:10)
    @test allunique(abs2, Vector{Any}(1:100))
    # These cases don't call the function at all:
    @test allunique(error, [])
    @test_skip allunique(error, [1]) # depends on updated code in Base to work
end
@testset "allequal(f, xs)" begin
    @test allequal(abs2, [3, -3])
    @test allequal(x -> 1, rand(3))
    @test !allequal(x -> rand(), [1,1,1])
    # tuples
    @test allequal(abs2, (3, -3))
    @test allequal(x -> 1, Tuple(rand(3)))
    @test !allequal(x -> rand(), (1,1,1))
    # These cases don't call the function at all:
    @test allequal(error, [])
    @test allequal(error, ())
    @test allequal(error, (x for x in 1:3 if false))
    @test_skip allequal(error, [1])  # fixed not by new code but by upgrades to old code
    @test allequal(error, (1,))
end

# https://github.com/JuliaLang/julia/pull/45052
@testset "VersionNumber no-op constructor" begin
    v = VersionNumber("1.2.3")
    @test VersionNumber(v) === v
end

# https://github.com/JuliaLang/julia/pull/47354
@testset "cycle(iter, n)"  begin
    using Base.Iterators: cycle
    @test collect(cycle(0:3, 2)) == [0, 1, 2, 3, 0, 1, 2, 3]
    @test collect(cycle(Iterators.filter(iseven, 1:4), 2)) == [2, 4, 2, 4]
    # @test collect(take(cycle(countfrom(11), 3), 4)) == 11:14  # this iterator is defined in Base's tests

    @test isempty(cycle(1:0)) == isempty(cycle(1:0, 3)) == true
    @test isempty(cycle(1:5, 0))
    @test isempty(cycle(Iterators.filter(iseven, 1:4), 0))

    @test eltype(cycle(0:3, 2)) === Int
    @test Base.IteratorEltype(cycle(0:3, 2)) == Base.HasEltype()

    Base.haslength(cycle(0:3, 2)) == false  # but not sure we should test these
    Base.IteratorSize(cycle(0:3, 2)) == Base.SizeUnknown()
end

# https://github.com/JuliaLang/julia/pull/39071
@testset "logrange" begin
    # basic idea
    @test logrange(2, 16, 4) ≈ [2, 4, 8, 16]
    @test logrange(1/8, 8.0, 7) ≈ [0.125, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0]
    @test logrange(1000, 1, 4) ≈ [1000, 100, 10, 1]
    @test logrange(1, 10^9, 19)[1:2:end] ≈ 10 .^ (0:9)

    # endpoints
    @test logrange(0.1f0, 100, 33)[1] === 0.1f0
    @test logrange(0.789, 123_456, 135_790)[[begin, end]] == [0.789, 123_456]
    @test logrange(nextfloat(0f0), floatmax(Float32), typemax(Int))[end] === floatmax(Float32)
    @test logrange(nextfloat(Float16(0)), floatmax(Float16), 66_000)[end] === floatmax(Float16)
    @test first(logrange(pi, 2pi, 3000)) === logrange(pi, 2pi, 3000)[1] === Float64(pi)
    if Int == Int64
        @test logrange(0.1, 1000, 2^54)[end] === 1000.0
    end

    # empty, only, constant
    @test first(logrange(1, 2, 0)) === 1.0
    @test last(logrange(1, 2, 0)) === 2.0
    @test collect(logrange(1, 2, 0)) == Float64[]
    @test only(logrange(2pi, 2pi, 1)) === logrange(2pi, 2pi, 1)[1] === 2pi
    @test logrange(1, 1, 3) == fill(1.0, 3)

    # subnormal Float64
    x = logrange(1e-320, 1e-300, 21) .* 1e300
    @test x ≈ logrange(1e-20, 1, 21) rtol=1e-6

    # types
    @test eltype(logrange(1, 10, 3)) == Float64
    @test eltype(logrange(1, 10, Int32(3))) == Float64
    @test eltype(logrange(1, 10f0, 3)) == Float32
    @test eltype(logrange(1f0, 10, 3)) == Float32
    @test eltype(logrange(1, big(10), 3)) == BigFloat
    @test logrange(big"0.3", big(pi), 50)[1] == big"0.3"
    @test logrange(big"0.3", big(pi), 50)[end] == big(pi)

    # more constructors
    @test logrange(1,2,length=3) === Compat.LogRange(1,2,3) == Compat.LogRange{Float64}(1,2,3)
    @test logrange(1f0, 2f0, length=3) == Compat.LogRange{Float32}(1,2,3)

    # errors
    @test_throws UndefKeywordError logrange(1, 10)  # no default length
    @test_throws ArgumentError logrange(1, 10, -1)  # negative length
    @test_throws ArgumentError logrange(1, 10, 1) # endpoints must not differ
    @test_throws DomainError logrange(1, -1, 3)   # needs complex numbers
    @test_throws DomainError logrange(-1, -2, 3)  # not supported, for now
    @test_throws MethodError logrange(1, 2+3im, length=4)  # not supported, for now
    @test_throws ArgumentError logrange(1, 10, 2)[true]  # bad index
    @test_throws BoundsError logrange(1, 10, 2)[3]
    @test_throws ArgumentError Compat.LogRange{Int}(1,4,5)  # no integer ranges
    @test_throws MethodError Compat.LogRange(1,4, length=5)  # type does not take keyword
    # (not sure if these should ideally be DomainError or ArgumentError)
    @test_throws DomainError logrange(1, Inf, 3)
    @test_throws DomainError logrange(0, 2, 3)
    @test_throws DomainError logrange(1, NaN, 3)
    @test_throws DomainError logrange(NaN, 2, 3)

    # printing
    @test repr(Compat.LogRange(1,2,3)) == "LogRange{Float64}(1.0, 2.0, 3)"  # like 2-arg show
    @test_skip repr("text/plain", Compat.LogRange(1,2,3)) == "3-element Compat.LogRange{Float64, Base.TwicePrecision{Float64}}:\n 1.0, 1.41421, 2.0"
    @test_skip repr("text/plain", Compat.LogRange(1,2,0)) == "LogRange{Float64}(1.0, 2.0, 0)"  # empty case
end

# https://github.com/JuliaLang/julia/pull/40995
@testset "chopprefix, chopsuffix" begin
    SubStr(s) = SubString("abc$(s)de", firstindex(s) + 3, lastindex(s) + 3)

    for S in (String, SubStr, Test.GenericString)
        for T in (String, SubStr, Test.GenericString, Regex)
            S === Test.GenericString && T === Regex && continue # not supported
            @test chopprefix(S("fo∀\n"), T("bog")) == "fo∀\n"
            @test chopprefix(S("fo∀\n"), T("\n∀foΔ")) == "fo∀\n"
            @test chopprefix(S("fo∀\n"), T("∀foΔ")) == "fo∀\n"
            @test chopprefix(S("fo∀\n"), T("f")) == "o∀\n"
            @test chopprefix(S("fo∀\n"), T("fo")) == "∀\n"
            @test chopprefix(S("fo∀\n"), T("fo∀")) == "\n"
            @test chopprefix(S("fo∀\n"), T("fo∀\n")) == ""
            @test chopprefix(S("\nfo∀"), T("bog")) == "\nfo∀"
            @test chopprefix(S("\nfo∀"), T("\n∀foΔ")) == "\nfo∀"
            @test chopprefix(S("\nfo∀"), T("\nfo∀")) == ""
            @test chopprefix(S("\nfo∀"), T("\n")) == "fo∀"
            @test chopprefix(S("\nfo∀"), T("\nf")) == "o∀"
            @test chopprefix(S("\nfo∀"), T("\nfo")) == "∀"
            @test chopprefix(S("\nfo∀"), T("\nfo∀")) == ""
            @test chopprefix(S(""), T("")) == ""
            @test chopprefix(S(""), T("asdf")) == ""
            @test chopprefix(S(""), T("∃∃∃")) == ""
            @test chopprefix(S("εfoo"), T("ε")) == "foo"
            @test chopprefix(S("ofoε"), T("o")) == "foε"
            @test chopprefix(S("∃∃∃∃"), T("∃")) == "∃∃∃"
            @test chopprefix(S("∃∃∃∃"), T("")) == "∃∃∃∃"

            @test chopsuffix(S("fo∀\n"), T("bog")) == "fo∀\n"
            @test chopsuffix(S("fo∀\n"), T("\n∀foΔ")) == "fo∀\n"
            @test chopsuffix(S("fo∀\n"), T("∀foΔ")) == "fo∀\n"
            @test chopsuffix(S("fo∀\n"), T("\n")) == "fo∀"
            @test chopsuffix(S("fo∀\n"), T("∀\n")) == "fo"
            @test chopsuffix(S("fo∀\n"), T("o∀\n")) == "f"
            @test chopsuffix(S("fo∀\n"), T("fo∀\n")) == ""
            @test chopsuffix(S("\nfo∀"), T("bog")) == "\nfo∀"
            @test chopsuffix(S("\nfo∀"), T("\n∀foΔ")) == "\nfo∀"
            @test chopsuffix(S("\nfo∀"), T("\nfo∀")) == ""
            @test chopsuffix(S("\nfo∀"), T("∀")) == "\nfo"
            @test chopsuffix(S("\nfo∀"), T("o∀")) == "\nf"
            @test chopsuffix(S("\nfo∀"), T("fo∀")) == "\n"
            @test chopsuffix(S("\nfo∀"), T("\nfo∀")) == ""
            @test chopsuffix(S(""), T("")) == ""
            @test chopsuffix(S(""), T("asdf")) == ""
            @test chopsuffix(S(""), T("∃∃∃")) == ""
            @test chopsuffix(S("fooε"), T("ε")) == "foo"
            @test chopsuffix(S("εofo"), T("o")) == "εof"
            @test chopsuffix(S("∃∃∃∃"), T("∃")) == "∃∃∃"
            @test chopsuffix(S("∃∃∃∃"), T("")) == "∃∃∃∃"
        end

        if S !== Test.GenericString
            @test chopprefix(S("∃∃∃b∃"), r"∃+") == "b∃"
            @test chopsuffix(S("∃b∃∃∃"), r"∃+") == "∃b"
        end

        @test isa(chopprefix(S("foo"), "fo"), SubString)
        @test isa(chopsuffix(S("foo"), "oo"), SubString)
    end
end

# https://github.com/JuliaLang/julia/pull/45793
@testset "insertdims" begin
     a = rand(8, 7)
     @test @inferred(insertdims(a, dims=1)) == @inferred(insertdims(a, dims=(1,))) == reshape(a, (1, 8, 7))
     @test @inferred(insertdims(a, dims=3))  == @inferred(insertdims(a, dims=(3,))) == reshape(a, (8, 7, 1))
     @test @inferred(insertdims(a, dims=(1, 3)))  == reshape(a, (1, 8, 1, 7))
     @test @inferred(insertdims(a, dims=(1, 2, 3)))  == reshape(a, (1, 1, 1, 8, 7))
     @test @inferred(insertdims(a, dims=(1, 4)))  == reshape(a, (1, 8, 7, 1))
     @test @inferred(insertdims(a, dims=(1, 3, 5)))  == reshape(a, (1, 8, 1, 7, 1))
     @test @inferred(insertdims(a, dims=(1, 2, 4, 6)))  == reshape(a, (1, 1, 8, 1, 7, 1))
     @test @inferred(insertdims(a, dims=(1, 3, 4, 6)))  == reshape(a, (1, 8, 1, 1, 7, 1))
     @test @inferred(insertdims(a, dims=(1, 4, 6, 3)))  == reshape(a, (1, 8, 1, 1, 7, 1))
     @test @inferred(insertdims(a, dims=(1, 3, 5, 6)))  == reshape(a, (1, 8, 1, 7, 1, 1))

     @test_throws ArgumentError insertdims(a, dims=(1, 1, 2, 3))
     @test_throws ArgumentError insertdims(a, dims=(1, 2, 2, 3))
     @test_throws ArgumentError insertdims(a, dims=(1, 2, 3, 3))
     @test_throws UndefKeywordError insertdims(a)
     @test_throws ArgumentError insertdims(a, dims=0)
     @test_throws ArgumentError insertdims(a, dims=(1, 2, 1))
     @test_throws ArgumentError insertdims(a, dims=4)
     @test_throws ArgumentError insertdims(a, dims=6)

     # insertdims and dropdims are inverses
     b = rand(1,1,1,5,1,1,7)
     for dims in [1, (1,), 2, (2,), 3, (3,), (1,3), (1,2,3), (1,2), (1,3,5), (1,2,5,6), (1,3,5,6), (1,3,5,6), (1,6,5,3)]
         @test dropdims(insertdims(a; dims); dims) == a
         @test insertdims(dropdims(b; dims); dims) == b
     end
end
    
# https://github.com/JuliaLang/julia/pull/54653: add Fix
@testset "Fix" begin
    function test_fix1(Fix1=Compat.Fix1)
        increment = Fix1(+, 1)
        @test increment(5) == 6
        @test increment(-1) == 0
        @test increment(0) == 1
        @test map(increment, [1, 2, 3]) == [2, 3, 4]

        concat_with_hello = Fix1(*, "Hello ")
        @test concat_with_hello("World!") == "Hello World!"
        # Make sure inference is good:
        @inferred concat_with_hello("World!")

        one_divided_by = Fix1(/, 1)
        @test one_divided_by(10) == 1/10.0
        @test one_divided_by(-5) == 1/-5.0

        return nothing
    end

    function test_fix2(Fix2=Compat.Fix2)
        return_second = Fix2((x, y) -> y, 999)
        @test return_second(10) == 999
        @inferred return_second(10)
        @test return_second(-5) == 999

        divide_by_two = Fix2(/, 2)
        @test map(divide_by_two, (2, 4, 6)) == (1.0, 2.0, 3.0)
        @inferred map(divide_by_two, (2, 4, 6))

        concat_with_world = Fix2(*, " World!")
        @test concat_with_world("Hello") == "Hello World!"
        @inferred concat_with_world("Hello World!")

        return nothing
    end

    # Test with normal Base.Fix1 and Base.Fix2
    test_fix1()
    test_fix2()

    # Now, repeat the Fix1 and Fix2 tests, but
    # with a Fix lambda function used in their place
    test_fix1((op, arg) -> Compat.Fix{1}(op, arg))
    test_fix2((op, arg) -> Compat.Fix{2}(op, arg))

    # Now, we do more complex tests of Fix:
    let Fix=Compat.Fix
        @testset "Argument Fixation" begin
            let f = (x, y, z) -> x + y * z
                fixed_f1 = Fix{1}(f, 10)
                @test fixed_f1(2, 3) == 10 + 2 * 3

                fixed_f2 = Fix{2}(f, 5)
                @test fixed_f2(1, 4) == 1 + 5 * 4

                fixed_f3 = Fix{3}(f, 3)
                @test fixed_f3(1, 2) == 1 + 2 * 3
            end
        end
        @testset "Helpful errors" begin
            let g = (x, y) -> x - y
                # Test minimum N
                fixed_g1 = Fix{1}(g, 100)
                @test fixed_g1(40) == 100 - 40

                # Test maximum N
                fixed_g2 = Fix{2}(g, 100)
                @test fixed_g2(150) == 150 - 100

                # One over
                fixed_g3 = Fix{3}(g, 100)
                @test_throws ArgumentError("expected at least 2 arguments to `Fix{3}`, but got 1") fixed_g3(1)
            end
        end
        @testset "Type Stability and Inference" begin
            let h = (x, y) -> x / y
                fixed_h = Fix{2}(h, 2.0)
                @test @inferred(fixed_h(4.0)) == 2.0
            end
        end
        @testset "Interaction with varargs" begin
            vararg_f = (x, y, z...) -> x + 10 * y + sum(z; init=zero(x))
            fixed_vararg_f = Fix{2}(vararg_f, 6)

            # Can call with variable number of arguments:
            @test fixed_vararg_f(1, 2, 3, 4) == 1 + 10 * 6 + sum((2, 3, 4))
            if VERSION >= v"1.7.0"
                @inferred fixed_vararg_f(1, 2, 3, 4)
            end
            @test fixed_vararg_f(5) == 5 + 10 * 6
            if VERSION >= v"1.7.0"
                @inferred fixed_vararg_f(5)
            end
        end
        @testset "Errors should propagate normally" begin
            error_f = (x, y) -> sin(x * y)
            fixed_error_f = Fix{2}(error_f, Inf)
            @test_throws DomainError fixed_error_f(10)
        end
        @testset "Chaining Fix together" begin
            f1 = Fix{1}(*, "1")
            f2 = Fix{1}(f1, "2")
            f3 = Fix{1}(f2, "3")
            @test f3() == "123"

            g1 = Fix{2}(*, "1")
            g2 = Fix{2}(g1, "2")
            g3 = Fix{2}(g2, "3")
            @test g3("") == "123"
        end
        @testset "Zero arguments" begin
            f = Fix{1}(x -> x, 'a')
            @test f() == 'a'
        end
        @testset "Dummy-proofing" begin
            @test_throws ArgumentError("expected `N` in `Fix{N}` to be integer greater than 0, but got 0") Fix{0}(>, 1)
            @test_throws ArgumentError("expected type parameter in `Fix` to be `Int`, but got `0.5::Float64`") Fix{0.5}(>, 1)
            @test_throws ArgumentError("expected type parameter in `Fix` to be `Int`, but got `1::UInt64`") Fix{UInt64(1)}(>, 1)
        end
        @testset "Specialize to structs not in `Base`" begin
            struct MyStruct
                x::Int
            end
            f = Fix{1}(MyStruct, 1)
            @test f isa Fix{1,Type{MyStruct},Int}
        end
    end
end

# https://github.com/JuliaLang/julia/pull/50795
@testset "filter on NamedTuple" begin
    @test filter(isodd, (a=1,b=2,c=3)) === (a=1, c=3)
    @test filter(i -> true, (;)) === (;)
    longnt = NamedTuple{ntuple(i -> Symbol(:a, i), 20)}(ntuple(identity, 20))
    @test filter(iseven, longnt) === NamedTuple{ntuple(i -> Symbol(:a, 2i), 10)}(ntuple(i -> 2i, 10))
    @test filter(x -> x<2, (longnt..., z=1.5)) === (a1=1, z=1.5)
end

# https://github.com/JuliaLang/julia/pull/58940
@testset "@__FUNCTION__" begin
    @testset "Basic usage" begin
        # @__FUNCTION__ in regular functions
        test_function_basic() = @__FUNCTION__
        @test test_function_basic() === test_function_basic
    end

    @testset "Recursion" begin
        # Factorial with @__FUNCTION__
        factorial_function(n) = n <= 1 ? 1 : n * (@__FUNCTION__)(n - 1)
        @test factorial_function(5) == 120

        # Fibonacci with @__FUNCTION__
        struct RecursiveCallableStruct; end
        @eval (::RecursiveCallableStruct)(n) = n <= 1 ? n : (@__FUNCTION__)(n-1) + (@__FUNCTION__)(n-2)
        @test RecursiveCallableStruct()(10) === 55

        # Anonymous function recursion
        @test (n -> n <= 1 ? 1 : n * (@__FUNCTION__)(n - 1))(5) == 120
    end

    @testset "Closures and nested functions" begin
        # Prevents boxed closures
        function make_closure()
            fib(n) = n <= 1 ? 1 : (@__FUNCTION__)(n - 1) + (@__FUNCTION__)(n - 2)
            return fib
        end
        Test.@inferred make_closure()
        closure = make_closure()
        @test closure(5) == 8
        Test.@inferred closure(5)

        # Complex closure of closures
        function f1()
            function f2()
                function f3()
                    return @__FUNCTION__
                end
                return (@__FUNCTION__), f3()
            end
            return (@__FUNCTION__), f2()...
        end
        Test.@inferred f1()
        @test f1()[1] === f1
        @test f1()[2] !== f1
        @test f1()[3] !== f1
        @test f1()[3]() === f1()[3]
        @test f1()[2]()[2]() === f1()[3]
    end

    @testset "Do blocks" begin
        function test_do_block()
            result = map([1, 2, 3]) do x
                return (@__FUNCTION__, x)
            end
            # All should refer to the same do-block function
            @test all(r -> r[1] === result[1][1], result)
            # Values should be different
            @test [r[2] for r in result] == [1, 2, 3]
            # It should be different than `test_do_block`
            @test result[1][1] !== test_do_block
        end
        test_do_block()
    end

    @testset "Keyword arguments" begin
        # @__FUNCTION__ with kwargs
        foo(; n) = n <= 1 ? 1 : n * (@__FUNCTION__)(; n = n - 1)
        @test foo(n = 5) == 120
    end

    @testset "Callable structs" begin
        # @__FUNCTION__ in callable structs
        @gensym A
        @eval module $A
            import ..@__FUNCTION__

            struct CallableStruct{T}; val::T; end
            (c::CallableStruct)() = @__FUNCTION__
        end
        @eval using .$A: CallableStruct
        c = CallableStruct(5)
        if isdefined(Base, Symbol("@__FUNCTION__"))
            @test c() === c
        else
            @test_broken c() === c
        end

        # In closures, var"#self#" should refer to the enclosing function,
        # NOT the enclosing struct instance
        struct CallableStruct2; end
        @eval function (obj::CallableStruct2)()
            function inner_func()
                @__FUNCTION__
            end
            inner_func
        end

        let cs = CallableStruct2()
            @test cs()() === cs()
            @test cs()() !== cs
        end

        # Accessing values via self-reference
        struct CallableStruct3
            value::Int
        end
        @eval (obj::CallableStruct3)() = @__FUNCTION__
        @eval (obj::CallableStruct3)(x) = (@__FUNCTION__).value + x

        let cs = CallableStruct3(42)
            if isdefined(Base, Symbol("@__FUNCTION__"))
                @test cs() === cs
                @test cs(10) === 52
            else
                @test_broken cs() === cs
                @test_broken cs(10) === 52
            end
        end

        # Callable struct with args and kwargs
        struct CallableStruct4
        end
        @eval function (obj::CallableStruct4)(x, args...; y=2, kws...)
            return (; func=(@__FUNCTION__), x, args, y, kws)
        end
        c = CallableStruct4()
        if isdefined(Base, Symbol("@__FUNCTION__"))
            @test c(1).func === c
            @test c(2, 3).args == (3,)
            @test c(2; y=4).y == 4
            @test c(2; y=4, a=5, b=6, c=7).kws[:c] == 7
        else
            @test_broken c(1).func === c
            @test_broken c(2, 3).args == (3,)
            @test_broken c(2; y=4).y == 4
            @test_broken c(2; y=4, a=5, b=6, c=7).kws[:c] == 7
        end
    end

    @testset "Special cases" begin
        # Generated functions
        let @generated foo2() = :(@__FUNCTION__)
            @test foo2() === foo2
        end

        # Should not access arg-map for local variables
        @gensym f
        @eval begin
            function $f end
            function ($f::typeof($f))()
                $f = 1
                @__FUNCTION__
            end
        end
        if isdefined(Base, Symbol("@__FUNCTION__"))
            @test @eval($f() === $f)
        else
            @test_broken @eval($f() === $f)
        end
    end

    if isdefined(Base, Symbol("@__FUNCTION__"))
        @testset "Error upon misuse" begin
            @gensym B
            @test_throws(
                "\"@__FUNCTION__\" can only be used inside a function",
                @eval(module $B; @__FUNCTION__; end)
            )

            @test_throws(
                "\"@__FUNCTION__\" not allowed inside comprehension or generator",
                @eval([(@__FUNCTION__) for _ in 1:10])
            )
        end
    end
end
