using BaseDirs
using Test

# This test suite assumes it is running on a "default" system,
# where no customisations apply.

usr = homedir()

@testset "Project compat" begin
    @test BaseDirs.Project("foo") == BaseDirs.App("foo")
    @test BaseDirs.Project("foo", org="org") == BaseDirs.App("foo", org="org")
    @test BaseDirs.Project("foo", "org", "qual") == BaseDirs.App("foo", "org", "qual")
end

if Sys.isapple()
    @testset "Dirs" begin
        @testset "Base" begin
            @test BaseDirs.DATA_HOME == "$usr/Library/Application Support"
            @test BaseDirs.DATA_DIRS == ["/Library/Application Support"]
            @test BaseDirs.CONFIG_HOME == "$usr/Library/Application Support"
            @test BaseDirs.CONFIG_DIRS == ["/Library/Application Support"]
            if occursin("$usr/.local/bin", get(ENV, "PATH", ""))
                @test BaseDirs.BIN_HOME == "$usr/.local/bin"
            else
                @test BaseDirs.BIN_HOME == "/usr/local/bin"
            end
            @test BaseDirs.STATE_HOME == "$usr/Library/Application Support"
            @test BaseDirs.CACHE_HOME == "$usr/Library/Caches"
            @test BaseDirs.RUNTIME_DIR == "$usr/Library/Application Support"
        end
        @testset "User" begin
            @test BaseDirs.DESKTOP_DIR == "$usr/Desktop"
            @test BaseDirs.DOWNLOAD_DIR == "$usr/Downloads"
            @test BaseDirs.DOCUMENTS_DIR == "$usr/Documents"
            @test BaseDirs.PROJECTS_DIR == "$usr/Projects"
            @test BaseDirs.MUSIC_DIR == "$usr/Music"
            @test BaseDirs.PICTURES_DIR == "$usr/Pictures"
            @test BaseDirs.VIDEOS_DIR == "$usr/Videos"
            @test BaseDirs.TEMPLATES_DIR == "$usr/Templates"
            @test BaseDirs.PUBLICSHARE_DIR == "$usr/Public"
        end
        @testset "Other" begin
            @test BaseDirs.APPLICATIONS_DIRS == ["/Applications"]
            @test BaseDirs.FONTS_DIRS == ["$usr/Library/Fonts", "/Library/Fonts", "/System/Library/Fonts", "/System/Library/Fonts/Supplemental", "/Network/Library/Fonts"]
        end
    end
    @testset "User" begin
        @test BaseDirs.User.data() == "$usr/Library/Application Support"
        @test BaseDirs.User.config() == "$usr/Library/Application Support"
        @test BaseDirs.User.bin() == BaseDirs.BIN_HOME
        @test BaseDirs.User.state() == "$usr/Library/Application Support"
        @test BaseDirs.User.cache() == "$usr/Library/Caches"
        @test BaseDirs.User.runtime() == "$usr/Library/Application Support"
        @test BaseDirs.User.desktop() == "$usr/Desktop"
        @test BaseDirs.User.downloads() == "$usr/Downloads"
        @test BaseDirs.User.documents() == "$usr/Documents"
        @test BaseDirs.User.projects() == "$usr/Projects"
        @test BaseDirs.User.music() == "$usr/Music"
        @test BaseDirs.User.pictures() == "$usr/Pictures"
        @test BaseDirs.User.videos() == "$usr/Videos"
        @test BaseDirs.User.templates() == "$usr/Templates"
        @test BaseDirs.User.public() == "$usr/Public"
        @test BaseDirs.User.fonts() == ["$usr/Library/Fonts"]
        @test BaseDirs.User.applications() == String[]
    end
    @testset "System" begin
        @test BaseDirs.System.data() == ["/Library/Application Support"]
        @test BaseDirs.System.config() == ["/Library/Application Support"]
        @test BaseDirs.System.fonts() == ["/Library/Fonts", "/System/Library/Fonts", "/System/Library/Fonts/Supplemental", "/Network/Library/Fonts"]
        @test BaseDirs.System.applications() == ["/Applications"]
    end
    @testset "Other" begin
        @test BaseDirs.data() == ["$usr/Library/Application Support", "/Library/Application Support"]
        @test BaseDirs.config() == ["$usr/Library/Application Support", "/Library/Application Support"]
        @test BaseDirs.fonts() == ["$usr/Library/Fonts", "/Library/Fonts", "/System/Library/Fonts", "/System/Library/Fonts/Supplemental", "/Network/Library/Fonts"]
        @test BaseDirs.applications() == ["/Applications"]
    end
    @testset "App" begin
        @test BaseDirs.applicationpath(BaseDirs.App("a")) == "lang.julia.a/"
        @test BaseDirs.applicationpath(BaseDirs.App("Hey There")) == "lang.julia.Hey-There/"
        @test BaseDirs.applicationpath(BaseDirs.App("foo"), :state, "$usr/Library/Application Support") == "lang.julia.foo/state/"
        @test BaseDirs.applicationpath(BaseDirs.App("foo"), :state, "$usr/.local/state") == "julia/foo/"
        @test BaseDirs.applicationpath(BaseDirs.App("Hey There"), :state, "$usr/Library/Application Support") == "lang.julia.Hey-There/state/"
        @test BaseDirs.applicationpath(BaseDirs.App("Hey There"), :state, "$usr/.local/state") == "julia/heythere/"
    end
    @test isnothing(BaseDirs.reload())
elseif Sys.isunix()
    uid = rstrip(read(`id -u`, String))
    @testset "Dirs" begin
        @testset "Base" begin
            @test BaseDirs.DATA_HOME == "$usr/.local/share"
            @test BaseDirs.DATA_DIRS == ["/usr/local/share", "/usr/share"]
            @test BaseDirs.CONFIG_HOME == "$usr/.config"
            @test BaseDirs.CONFIG_DIRS == ["/etc/xdg"]
            @test BaseDirs.BIN_HOME == "$usr/.local/bin"
            @test BaseDirs.STATE_HOME == "$usr/.local/state"
            @test BaseDirs.CACHE_HOME == "$usr/.cache"
            @test BaseDirs.RUNTIME_DIR == "/run/user/$uid"
        end
        @testset "User" begin
            @test BaseDirs.DESKTOP_DIR == "$usr/Desktop"
            @test BaseDirs.DOWNLOAD_DIR == "$usr/Downloads"
            @test BaseDirs.DOCUMENTS_DIR == "$usr/Documents"
            @test BaseDirs.PROJECTS_DIR == "$usr/Projects"
            @test BaseDirs.MUSIC_DIR == "$usr/Music"
            @test BaseDirs.PICTURES_DIR == "$usr/Pictures"
            @test BaseDirs.VIDEOS_DIR == "$usr/Videos"
            @test BaseDirs.TEMPLATES_DIR == "$usr/Templates"
            @test BaseDirs.PUBLICSHARE_DIR == "$usr/Public"
        end
        @testset "Other" begin
            @test BaseDirs.APPLICATIONS_DIRS == ["$usr/.local/share/applications", "/usr/local/share/applications", "/usr/share/applications"]
            @test BaseDirs.FONTS_DIRS == ["$usr/.local/share/fonts", "$usr/.fonts", "/usr/local/share/fonts", "/usr/share/fonts"]
        end
    end
    @testset "User" begin
        @test BaseDirs.User.data() == "$usr/.local/share"
        @test BaseDirs.User.config() == "$usr/.config"
        @test BaseDirs.User.bin() == "$usr/.local/bin"
        @test BaseDirs.User.state() == "$usr/.local/state"
        @test BaseDirs.User.cache() == "$usr/.cache"
        @test BaseDirs.User.runtime() == "/run/user/$uid"
        @test BaseDirs.User.desktop() == "$usr/Desktop"
        @test BaseDirs.User.downloads() == "$usr/Downloads"
        @test BaseDirs.User.documents() == "$usr/Documents"
        @test BaseDirs.User.projects() == "$usr/Projects"
        @test BaseDirs.User.music() == "$usr/Music"
        @test BaseDirs.User.pictures() == "$usr/Pictures"
        @test BaseDirs.User.videos() == "$usr/Videos"
        @test BaseDirs.User.templates() == "$usr/Templates"
        @test BaseDirs.User.public() == "$usr/Public"
        @test BaseDirs.User.fonts() == ["$usr/.local/share/fonts", "$usr/.fonts"]
        @test BaseDirs.User.applications() == ["$usr/.local/share/applications"]
    end
    @testset "User (customised)" begin
        userdirs_jpn = raw"""
        XDG_DESKTOP_DIR="$HOME/デスクトップ"
        XDG_DOWNLOAD_DIR="$HOME/ダウンロード"
        XDG_TEMPLATES_DIR="$HOME/テンプレート"
        XDG_PUBLICSHARE_DIR="$HOME/公開"
        XDG_DOCUMENTS_DIR="$HOME/ドキュメント"
        XDG_MUSIC_DIR="$HOME/ミュージック"
        XDG_PICTURES_DIR="$HOME/ピクチャ"
        XDG_PROJECTS_DIR="$HOME/プロジェクト"
        XDG_VIDEOS_DIR="$HOME/ビデオ"
        """
        userdirs_file = joinpath(BaseDirs.CONFIG_HOME, "user-dirs.dirs")
        isfile(userdirs_file) &&
            mv(userdirs_file, userdirs_file * ".backup")
        write(userdirs_file, userdirs_jpn)
        @test BaseDirs.reload() === nothing
        @test BaseDirs.User.desktop() == "$usr/デスクトップ"
        @test BaseDirs.User.downloads() == "$usr/ダウンロード"
        @test BaseDirs.User.documents() == "$usr/ドキュメント"
        @test BaseDirs.User.projects() == "$usr/プロジェクト"
        @test BaseDirs.User.music() == "$usr/ミュージック"
        @test BaseDirs.User.pictures() == "$usr/ピクチャ"
        @test BaseDirs.User.videos() == "$usr/ビデオ"
        @test BaseDirs.User.templates() == "$usr/テンプレート"
        @test BaseDirs.User.public() == "$usr/公開"
        rm(userdirs_file)
        isfile(userdirs_file * ".backup") &&
            mv(userdirs_file * ".backup", userdirs_file)
    end
    @testset "System" begin
        @test BaseDirs.System.data() == ["/usr/local/share", "/usr/share"]
        @test BaseDirs.System.config() == ["/etc/xdg"]
        @test BaseDirs.System.fonts() == ["/usr/local/share/fonts", "/usr/share/fonts"]
        @test BaseDirs.System.applications() == ["/usr/local/share/applications", "/usr/share/applications"]
    end
    @testset "Other" begin
        @test BaseDirs.data() == ["$usr/.local/share", "/usr/local/share", "/usr/share"]
        @test BaseDirs.config() == ["$usr/.config", "/etc/xdg"]
        @test BaseDirs.fonts() == ["$usr/.local/share/fonts", "$usr/.fonts", "/usr/local/share/fonts", "/usr/share/fonts"]
        @test BaseDirs.applications() == ["$usr/.local/share/applications", "/usr/local/share/applications", "/usr/share/applications"]
    end
    @testset "App" begin
        @test BaseDirs.applicationpath(BaseDirs.App("a")) == "julia/a/"
        @test BaseDirs.applicationpath(BaseDirs.App("a"), "?") == "julia/a/"
        @test BaseDirs.applicationpath(BaseDirs.App("Hey There")) == "julia/heythere/"
    end
    @testset "Directory existence" begin
        withenv("XDG_DATA_DIRS" => "/root/forbidden:/nonexistent:/usr/share") do
            BaseDirs.reload()
            @test BaseDirs.System.data(existent = true) == ["/usr/share"]
        end
        BaseDirs.reload()
    end
    @testset "XDG_*_DIRS parsing" begin
        @testset "separator" begin
            withenv("XDG_DATA_DIRS" => "/opt/a:/opt/b") do
                BaseDirs.reload()
                @test BaseDirs.System.data() == ["/opt/a", "/opt/b"]
            end
        end
        @testset "trailing separator stripped" begin
            withenv("XDG_DATA_DIRS" => "/opt/a/:/opt/b//") do
                BaseDirs.reload()
                @test BaseDirs.System.data() == ["/opt/a", "/opt/b"]
            end
            withenv("XDG_DATA_HOME" => "/opt/home//") do
                BaseDirs.reload()
                @test BaseDirs.DATA_HOME == "/opt/home"
            end
        end
        BaseDirs.reload()
    end
    @testset "File creation" begin
        filepath = "$usr/.local/share/julia/a/my_base_dirs_julia_test_file"
        @test BaseDirs.User.data(BaseDirs.App("a"), "my_base_dirs_julia_test_file") == filepath
        rm(filepath, force=true)
        try
            @test BaseDirs.User.data(BaseDirs.App("a"), "my_base_dirs_julia_test_file", create=true) == filepath
            @test isfile(filepath)
        finally
            rm(filepath, force=true)
        end
    end
    @testset "Folder creation" begin
        folderpath = "$usr/.local/share/julia/a/my_base_dirs_julia_test_dir/"
        @test BaseDirs.User.data(BaseDirs.App("a"), "my_base_dirs_julia_test_dir/") == folderpath
        rm(folderpath, force=true)
        try
            @test BaseDirs.User.data(BaseDirs.App("a"), "my_base_dirs_julia_test_dir/", create=true) == folderpath
            @test isdir(folderpath)
        finally
            rm(folderpath, force=true)
        end
    end
    @testset "Executable creation" begin
        rm("$usr/.local/bin/my_base_dirs_julia_test_binary", force=true)
        try
            exec = BaseDirs.User.bin("my_base_dirs_julia_test_binary", create=true)
            @test exec == "$usr/.local/bin/my_base_dirs_julia_test_binary"
            @test isfile(exec)
            @test stat(exec).mode & 0o001 != 0
            @test exec == BaseDirs.User.bin("my_base_dirs_julia_test_binary", create=true)
            @test exec == BaseDirs.User.bin(BaseDirs.App("my_base_dirs_julia_test_binary"))
        finally
            rm("$usr/.local/bin/my_base_dirs_julia_test_binary", force=true)
        end
    end
    @test isnothing(BaseDirs.reload())
    @testset "User dirs" begin
        isfile("$usr/.config/user-dirs.dirs") &&
            mv("$usr/.config/user-dirs.dirs", "$usr/.config/user-dirs.dirs.backup")
        try
            write("$usr/.config/user-dirs.dirs",
                  """
                  XDG_DESKTOP_DIR="\$HOME/Workbench"
                  # A commented line
                  an invalid line
                  XDG_DOWNLOAD_DIR=/tmp/Downloads
                  XDG_TEMPLATES_DIR=/=Templates=
                  XDG_PICTURES_DIR="\$HOME/\\xZZ"
                  XDG_VIDEOS_DIR="\$HOME/Films"
                  XDG_INVALID="???"
                  """)
            @test isnothing(BaseDirs.reload())
            @test BaseDirs.User.desktop() == "$usr/Workbench"
            @test BaseDirs.User.downloads() == "/tmp/Downloads"
            @test BaseDirs.User.templates() == "/=Templates="
            @test BaseDirs.User.pictures() == "$usr/Pictures"
            @test BaseDirs.User.videos() == "$usr/Films"
        finally
            rm("$usr/.config/user-dirs.dirs", force=true)
            isfile("$usr/.config/user-dirs.dirs.backup") &&
                mv("$usr/.config/user-dirs.dirs.backup", "$usr/.config/user-dirs.dirs")
        end
    end
    @testset "System user-dirs.defaults" begin
        sysdir = mktempdir()
        write(joinpath(sysdir, "user-dirs.defaults"),
              """
              # Default settings for user directories
              DESKTOP=Desktop
              DOWNLOAD=Downloads
              MUSIC=Documents/Music
              UNKNOWN=Nope
              EMPTY=
              """)
        hadhome = isfile("$usr/.config/user-dirs.dirs")
        hadhome && mv("$usr/.config/user-dirs.dirs", "$usr/.config/user-dirs.dirs.backup")
        try
            withenv("XDG_CONFIG_DIRS" => sysdir) do
                @test isnothing(BaseDirs.reload())
                @test BaseDirs.User.desktop() == "$usr/Desktop"
                @test BaseDirs.User.downloads() == "$usr/Downloads"
                @test BaseDirs.User.music() == "$usr/Documents/Music"
                # Unrecognised/empty keys fall back to built-in defaults.
                @test BaseDirs.User.videos() == "$usr/Videos"
            end
            # A per-user entry overrides the system default.
            write("$usr/.config/user-dirs.dirs", "XDG_DESKTOP_DIR=\"\$HOME/Workbench\"\n")
            withenv("XDG_CONFIG_DIRS" => sysdir) do
                BaseDirs.reload()
                @test BaseDirs.User.desktop() == "$usr/Workbench"
                @test BaseDirs.User.downloads() == "$usr/Downloads"
            end
        finally
            rm("$usr/.config/user-dirs.dirs", force=true)
            hadhome && mv("$usr/.config/user-dirs.dirs.backup", "$usr/.config/user-dirs.dirs")
            rm(sysdir, recursive=true, force=true)
            BaseDirs.reload()
        end
    end
    @testset "Systemd Integration" begin
        has_systemd = success(`systemctl --version`)
        can_run_service = success(`systemctl --user show-environment`)
        if !has_systemd || !can_run_service
            @test_skip "Systemd integration tests require systemd and user service support"
        else
            tmpdir = mktempdir()
            test_script_plain = joinpath(tmpdir, "test_plain.jl")
            write(test_script_plain, """
            using BaseDirs

            println("SYSTEMD_SERVICE=", BaseDirs.SYSTEMD_SERVICE)
            println("User.config()=", BaseDirs.User.config())
            println("User.data()=", BaseDirs.User.data())
            println("User.state()=", BaseDirs.User.state())
            println("User.cache()=", BaseDirs.User.cache())
            println("User.runtime()=", BaseDirs.User.runtime())
            """)

            test_script_project = joinpath(tmpdir, "test_project.jl")
            write(test_script_project, """
            using BaseDirs

            proj = BaseDirs.App("testapp", org="testorg")

            println("SYSTEMD_SERVICE=", BaseDirs.SYSTEMD_SERVICE)
            println("User.config(proj)=", BaseDirs.User.config(proj))
            println("User.data(proj)=", BaseDirs.User.data(proj))
            println("User.state(proj)=", BaseDirs.User.state(proj))
            println("User.cache(proj)=", BaseDirs.User.cache(proj))
            println("User.runtime(proj)=", BaseDirs.User.runtime(proj))
            """)

            function parse_output(output::String)
                result = Dict{String, String}()
                for line in split(output, '\n')
                    if occursin('=', line)
                        key, value = split(line, '=', limit=2)
                        result[strip(key)] = strip(value)
                    end
                end
                result
            end

            julia_cmd = filter(arg -> !any(a -> startswith(arg, a), ("--color", "--depwarn", "--project", "--startup-file")), collect(Base.julia_cmd()))
            dummy_systemd_envvars = ["-E", "CONFIGURATION_DIRECTORY=C",
                                     "-E", "LOGS_DIRECTORY=L",
                                     "-E", "STATE_DIRECTORY=S",
                                     "-E", "CACHE_DIRECTORY=H",
                                     "-E", "RUNTIME_DIRECTORY=R"]

            @testset "Plain script standalone" begin
                standalone_output = read(`systemd-run --user --scope $dummy_systemd_envvars -- $julia_cmd --project=$(dirname(@__DIR__)) $test_script_plain`, String)
                standalone = parse_output(standalone_output)
                @test standalone["SYSTEMD_SERVICE"] == "false"
                @test standalone["User.config()"] == "$usr/.config"
                @test standalone["User.state()"] == "$usr/.local/state"
                @test standalone["User.cache()"] == "$usr/.cache"
                @test standalone["User.runtime()"] == "/run/user/$uid"
                @test standalone["User.data()"] == "$usr/.local/share"
            end
            @testset "Plain script as systemd service" begin
                service_name = "basedirs-test-plain-$(getpid())"
                service_file = expanduser("/etc/systemd/system/$(service_name).service")
                tmpfile = tempname()
                try
                    write(tmpfile, """
                    [Unit]
                    Description=BaseDirs Test Plain

                    [Service]
                    Type=oneshot
                    ExecStart=$(join(julia_cmd, ' ')) --project=$(dirname(@__DIR__)) $test_script_plain
                    ConfigurationDirectory=$service_name
                    StateDirectory=$service_name
                    CacheDirectory=$service_name
                    RuntimeDirectory=$service_name
                    StandardOutput=journal
                    StandardError=journal
                    """)
                    run(`sudo mkdir -p $(dirname(service_file))`)
                    run(`sudo cp $tmpfile $service_file`)
                    run(`sudo systemctl daemon-reload`)
                    run(`sudo systemctl start $(service_name).service`)
                    service_output = read(`journalctl -u $(service_name).service -n 15 --no-pager -o cat`, String)
                    service = parse_output(service_output)
                    @test service["SYSTEMD_SERVICE"] == "true"
                    @test service["User.config()"]   == "/etc/$service_name"
                    @test service["User.state()"]    == "/var/lib/$service_name"
                    @test service["User.cache()"]    == "/var/cache/$service_name"
                    @test service["User.runtime()"]  == "/run/$service_name"
                finally
                    run(`sudo systemctl stop $(service_name).service`)
                    run(`sudo rm $service_file`)
                    run(`sudo systemctl daemon-reload`)
                end
            end
            @testset "Project script standalone" begin
                standalone_output = read(`systemd-run --user --scope $dummy_systemd_envvars -- $julia_cmd --startup-file=no --project=$(dirname(@__DIR__)) $test_script_project`, String)
                standalone = parse_output(standalone_output)
                @test standalone["SYSTEMD_SERVICE"] == "false"
                @test occursin("testorg", standalone["User.config(proj)"])
            end
            @testset "Project script as systemd service" begin
                service_name = "basedirs-test-project-$(getpid())"
                service_file = expanduser("/etc/systemd/system/$(service_name).service")
                tmpfile = tempname()
                try
                    write(tmpfile, """
                    [Unit]
                    Description=BaseDirs Test Project

                    [Service]
                    Type=oneshot
                    ExecStart=$(join(julia_cmd, ' ')) --startup-file=no --project=$(dirname(@__DIR__)) $test_script_project
                    ConfigurationDirectory=$service_name
                    StateDirectory=$service_name
                    CacheDirectory=$service_name
                    RuntimeDirectory=$service_name
                    StandardOutput=journal
                    StandardError=journal
                    """)
                    run(`sudo cp $tmpfile $service_file`)
                    run(`sudo systemctl daemon-reload`)
                    run(`sudo systemctl start $(service_name).service`)
                    # Get service output from journal
                    service_output = read(`journalctl -u $(service_name).service -n 15 --no-pager -o cat`, String)
                    service = parse_output(service_output)
                    # Verify systemd detection
                    @test service["SYSTEMD_SERVICE"] == "true"
                    @test !occursin("testorg", service["User.config(proj)"])
                    @test chopsuffix(service["User.config(proj)"],  "/") == "/etc/$service_name"
                    @test chopsuffix(service["User.state(proj)"],   "/") == "/var/lib/$service_name"
                    @test chopsuffix(service["User.cache(proj)"],   "/") == "/var/cache/$service_name"
                    @test chopsuffix(service["User.runtime(proj)"], "/") == "/run/$service_name"
                finally
                    run(`sudo systemctl stop $(service_name).service`)
                    run(`sudo rm $service_file`)
                    run(`sudo systemctl daemon-reload`)
                end
            end
        end
    end
elseif Sys.iswindows()
    @testset "OS specific" begin
        fieldvals(x::T) where {T} = ntuple(fieldcount(T)) do i
            getfield(x, i)
        end
        @test fieldvals(BaseDirs.relevantfolders()) == fieldvals(BaseDirs.RelevantFolders(
            "C:", "C:\\Windows", "C:\\ProgramData",
            "$usr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs",
            "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs",
            "$usr", "$usr\\AppData\\Roaming", "$usr\\AppData\\Local",
            "$usr\\Desktop", "$usr\\Downloads", "$usr\\Documents",
            "$usr\\Music", "$usr\\Pictures", "$usr\\Videos",
            "$usr\\AppData\\Roaming\\Microsoft\\Windows\\Templates",
            "C:\\Users\\Public", "C:\\Windows\\Fonts"))
    end
    @testset "Dirs" begin
        @testset "Base" begin
            @test BaseDirs.DATA_HOME == "$usr\\AppData\\Roaming"
            @test BaseDirs.DATA_DIRS == ["C:\\ProgramData"]
            @test BaseDirs.CONFIG_HOME == "$usr\\AppData\\Roaming"
            @test BaseDirs.CONFIG_DIRS == ["C:\\ProgramData"]
            @test BaseDirs.BIN_HOME == "$usr\\bin"
            @test BaseDirs.STATE_HOME == "$usr\\AppData\\Local"
            @test BaseDirs.CACHE_HOME == "$usr\\AppData\\Local\\cache"
            @test BaseDirs.RUNTIME_DIR == "$usr\\AppData\\Local"
        end
        @testset "User" begin
            @test BaseDirs.DESKTOP_DIR == "$usr\\Desktop"
            @test BaseDirs.DOWNLOAD_DIR == "$usr\\Downloads"
            @test BaseDirs.DOCUMENTS_DIR == "$usr\\Documents"
            @test BaseDirs.PROJECTS_DIR == "$usr\\Projects"
            @test BaseDirs.MUSIC_DIR == "$usr\\Music"
            @test BaseDirs.PICTURES_DIR == "$usr\\Pictures"
            @test BaseDirs.VIDEOS_DIR == "$usr\\Videos"
            @test BaseDirs.TEMPLATES_DIR == "$usr\\AppData\\Roaming\\Microsoft\\Windows\\Templates"
            @test BaseDirs.PUBLICSHARE_DIR == "C:\\Users\\Public"
        end
        @testset "Other" begin
            @test BaseDirs.APPLICATIONS_DIRS == ["$usr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs", "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs"]
            @test BaseDirs.FONTS_DIRS == ["C:\\Windows\\Fonts", "$usr\\AppData\\Local\\Microsoft\\Windows\\Fonts"]
        end
    end
    @testset "User" begin
        @test BaseDirs.User.data() == "$usr\\AppData\\Roaming"
        @test BaseDirs.User.config() == "$usr\\AppData\\Roaming"
        @test BaseDirs.User.bin() == "$usr\\bin"
        @test BaseDirs.User.state() == "$usr\\AppData\\Local"
        @test BaseDirs.User.cache() == "$usr\\AppData\\Local\\cache"
        @test BaseDirs.User.runtime() == "$usr\\AppData\\Local"
        @test BaseDirs.User.desktop() == "$usr\\Desktop"
        @test BaseDirs.User.downloads() == "$usr\\Downloads"
        @test BaseDirs.User.documents() == "$usr\\Documents"
        @test BaseDirs.User.projects() == "$usr\\Projects"
        @test BaseDirs.User.music() == "$usr\\Music"
        @test BaseDirs.User.pictures() == "$usr\\Pictures"
        @test BaseDirs.User.videos() == "$usr\\Videos"
        @test BaseDirs.User.templates() == "$usr\\AppData\\Roaming\\Microsoft\\Windows\\Templates"
        @test BaseDirs.User.public() == "C:\\Users\\Public"
        @test BaseDirs.User.fonts() == ["$usr\\AppData\\Local\\Microsoft\\Windows\\Fonts"]
        @test BaseDirs.User.applications() == ["$usr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs"]
    end
    @testset "System" begin
        @test BaseDirs.System.data() == ["C:\\ProgramData"]
        @test BaseDirs.System.config() == ["C:\\ProgramData"]
        @test BaseDirs.System.fonts() == ["C:\\Windows\\Fonts"]
        @test BaseDirs.System.applications() == ["C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs"]
    end
    @testset "Others" begin
        @test BaseDirs.data() == ["$usr\\AppData\\Roaming", "C:\\ProgramData"]
        @test BaseDirs.config() == ["$usr\\AppData\\Roaming", "C:\\ProgramData"]
        @test BaseDirs.fonts() == ["C:\\Windows\\Fonts", "$usr\\AppData\\Local\\Microsoft\\Windows\\Fonts"]
        @test BaseDirs.applications() == ["$usr\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs", "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs"]
    end
    @testset "Projects" begin
        @test BaseDirs.applicationpath(BaseDirs.App("a")) == "julia\\a\\"
        @test BaseDirs.applicationpath(BaseDirs.App("a", org="b")) == "b\\a\\"
        @test BaseDirs.applicationpath(BaseDirs.App("a"), :data, BaseDirs.DATA_HOME) == "julia\\a\\data\\"
        if BaseDirs.CONFIG_HOME != BaseDirs.DATA_HOME
            @test BaseDirs.applicationpath(BaseDirs.App("a"), :config, BaseDirs.CONFIG_HOME) == "julia\\a\\config\\"
        end
        @test BaseDirs.applicationpath(BaseDirs.App("a"), :cache, BaseDirs.CACHE_HOME) == "julia\\a\\cache\\"
        @test BaseDirs.applicationpath(BaseDirs.App("a"), :state, BaseDirs.STATE_HOME) == "julia\\a\\state\\"
    end
    @testset "XDG_*_DIRS parsing" begin
        @testset "separator" begin
            withenv("XDG_DATA_DIRS" => "C:\\A;D:\\B") do
                BaseDirs.reload()
                @test BaseDirs.System.data() == ["C:\\A", "D:\\B"]
            end
        end
        @testset "trailing separator stripped" begin
            withenv("XDG_DATA_DIRS" => "C:\\A\\;D:\\B/") do
                BaseDirs.reload()
                @test BaseDirs.System.data() == ["C:\\A", "D:\\B"]
            end
            withenv("XDG_DATA_HOME" => "C:\\Home\\/") do
                BaseDirs.reload()
                @test BaseDirs.DATA_HOME == "C:\\Home"
            end
        end
    end
    @test isnothing(BaseDirs.reload())
end

@static if VERSION < v"1.12"
    @test_skip "Precompilation warning tests require Julia 1.12 or later, and misbehave on Windows"
else
    @testset "Precompilation warnings" begin
        @test_warn "is likely misplaced" BaseDirs.@promise_no_assign 1+1
        function precompile_output(proj::String)
            out = IOBuffer()
            err = IOBuffer()
            for dep in DEPOT_PATH
                projdir = joinpath(dep, "compiled", string('v', VERSION.major, '.', VERSION.minor), basename(proj))
                isdir(projdir) && rm(projdir; force=true, recursive=true)
            end
            julia_cmd = filter(arg -> !any(a -> startswith(arg, a), ("--code-coverage", "--color", "--depwarn", "--project", "--startup-file")), collect(Base.julia_cmd()))
            precompile_cmd = addenv(`$julia_cmd --startup-file=no --color=no --project=$proj -e "using Pkg; Pkg.precompile()"`,
                                    "JULIA_LOAD_PATH" => ifelse(Sys.iswindows(), ";", ":"))
            try
                run(pipeline(precompile_cmd, stdout=out, stderr=err))
            catch
                error("Precompilation failed!\nCMD:\n$precompile_cmd\n\nSTDOUT:\n$(String(take!(out)))\nSTDERR:\n$(String(take!(err)))")
            end
            (stdout = String(take!(out)), stderr = String(take!(err)))
        end
        precompile_badassign = precompile_output(joinpath(@__DIR__, "BaseDirs_TestBadAssign"))
        @test contains(precompile_badassign.stderr, "Warning: A base directory is being computed during precompilation.")
        precompile_promiseassign = precompile_output(joinpath(@__DIR__, "BaseDirs_TestPromiseAssign"))
        @test !contains(precompile_promiseassign.stderr, "Warning:")
    end
end
