require "spec"
require "socket"
require "../../support/win32"
require "spec/helpers/string"

describe Socket::Address do
  describe ".parse" do
    it "accepts URI" do
      address = Socket::Address.parse URI.parse("tcp://192.168.0.1:8081")
      address.should eq Socket::IPAddress.new("192.168.0.1", 8081)
    end

    it "parses TCP" do
      address = Socket::Address.parse "tcp://192.168.0.1:8081"
      address.should eq Socket::IPAddress.new("192.168.0.1", 8081)
    end

    it "parses UDP" do
      address = Socket::Address.parse "udp://192.168.0.1:8081"
      address.should eq Socket::IPAddress.new("192.168.0.1", 8081)
    end

    it "parses UNIX" do
      address = Socket::Address.parse "unix://socket.sock"
      address.should eq Socket::UNIXAddress.new("socket.sock")
    end

    it "fails with unknown scheme" do
      expect_raises(Socket::Error, "Unsupported address type: ssl") do
        Socket::Address.parse "ssl://192.168.0.1:8081"
      end
    end
  end
end

describe Socket::IPAddress do
  c_port = {% if IO::ByteFormat::NetworkEndian != IO::ByteFormat::SystemEndian %}
             36895 # 0x901F
           {% else %}
             8080 # 0x1F90
           {% end %}

  it "transforms an IPv4 address into a C struct and back" do
    addr1 = Socket::IPAddress.new("127.0.0.1", 8080)

    addr1_c = addr1.to_unsafe
    addr1_c.as(LibC::SockaddrIn*).value.sin_port.should eq(c_port)

    addr2 = Socket::IPAddress.from(addr1_c, addr1.size)
    addr2.family.should eq(addr1.family)
    addr2.port.should eq(addr1.port)
    typeof(addr2.address).should eq(String)
    addr2.address.should eq(addr1.address)
    addr2.should eq(Socket::IPAddress.from(addr1_c))
  end

  it "transforms an IPv6 address into a C struct and back" do
    addr1 = Socket::IPAddress.new("2001:db8:8714:3a90::12", 8080)

    addr1_c = addr1.to_unsafe
    addr1_c.as(LibC::SockaddrIn6*).value.sin6_port.should eq(c_port)

    addr2 = Socket::IPAddress.from(addr1_c, addr1.size)
    addr2.family.should eq(addr1.family)
    addr2.port.should eq(addr1.port)
    typeof(addr2.address).should eq(String)
    addr2.address.should eq(addr1.address)
    addr2.should eq(Socket::IPAddress.from(addr1_c))
  end

  it "won't resolve domains" do
    expect_raises(Socket::Error, /Invalid IP address/) do
      Socket::IPAddress.new("localhost", 1234)
    end
  end

  it "errors on out of range port numbers" do
    expect_raises(Socket::Error, /Invalid port number/) do
      Socket::IPAddress.new("localhost", -1)
    end

    expect_raises(Socket::Error, /Invalid port number/) do
      Socket::IPAddress.new("localhost", 65536)
    end
  end

  it "#to_s" do
    assert_prints Socket::IPAddress.v4(UInt8.static_array(127, 0, 0, 1), 80).to_s, "127.0.0.1:80"

    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0x8714, 0x3a90, 0, 0, 0, 0x12), 443).to_s, "[2001:db8:8714:3a90::12]:443"
    assert_prints Socket::IPAddress.v6(UInt16.static_array(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 0xffff).to_s, "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535"
    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 1, 1, 1, 1, 1), 443).to_s, "[2001:db8:0:1:1:1:1:1]:443"
    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0, 0, 1, 0, 0, 0, 1), 443).to_s, "[2001:0:0:1::1]:443"
    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0, 0, 0, 1, 0, 0, 1), 443).to_s, "[2001::1:0:0:1]:443"
    assert_prints Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 0, 1, 0, 0, 1), 443).to_s, "[2001:db8::1:0:0:1]:443"
    assert_prints Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 1), 443).to_s, "[::1]:443"
    assert_prints Socket::IPAddress.v6(UInt16.static_array(1, 0, 0, 0, 0, 0, 0, 0), 443).to_s, "[1::]:443"
    assert_prints Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 0), 443).to_s, "[::]:443"

    assert_prints Socket::IPAddress.v4_mapped_v6(UInt8.static_array(0, 0, 0, 0), 443).to_s, "[::ffff:0.0.0.0]:443"
    assert_prints Socket::IPAddress.v4_mapped_v6(UInt8.static_array(192, 168, 1, 15), 443).to_s, "[::ffff:192.168.1.15]:443"

    assert_prints Socket::IPAddress.new("0:0:0:0:0:0:0:1", 443).to_s, "[::1]:443"
    assert_prints Socket::IPAddress.new("0:0:0:0:0:ffff:c0a8:010f", 443).to_s, "[::ffff:192.168.1.15]:443"
    assert_prints Socket::IPAddress.new("::ffff:0:0", 443).to_s, "[::ffff:0.0.0.0]:443"
  end

  it "#address" do
    Socket::IPAddress.v4(UInt8.static_array(127, 0, 0, 1), 80).address.should eq "127.0.0.1"

    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0x8714, 0x3a90, 0, 0, 0, 0x12), 443).address.should eq "2001:db8:8714:3a90::12"
    Socket::IPAddress.v6(UInt16.static_array(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 0xffff).address.should eq "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 1, 1, 1, 1, 1), 443).address.should eq "2001:db8:0:1:1:1:1:1"
    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0, 0, 1, 0, 0, 0, 1), 443).address.should eq "2001:0:0:1::1"
    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0, 0, 0, 1, 0, 0, 1), 443).address.should eq "2001::1:0:0:1"
    Socket::IPAddress.v6(UInt16.static_array(0x2001, 0xdb8, 0, 0, 1, 0, 0, 1), 443).address.should eq "2001:db8::1:0:0:1"
    Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 1), 443).address.should eq "::1"
    Socket::IPAddress.v6(UInt16.static_array(1, 0, 0, 0, 0, 0, 0, 0), 443).address.should eq "1::"
    Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 0), 443).address.should eq "::"

    Socket::IPAddress.v4_mapped_v6(UInt8.static_array(0, 0, 0, 0), 443).address.should eq "::ffff:0.0.0.0"
    Socket::IPAddress.v4_mapped_v6(UInt8.static_array(192, 168, 1, 15), 443).address.should eq "::ffff:192.168.1.15"

    Socket::IPAddress.new("0:0:0:0:0:0:0:1", 443).address.should eq "::1"
    Socket::IPAddress.new("0:0:0:0:0:ffff:c0a8:010f", 443).address.should eq "::ffff:192.168.1.15"
    Socket::IPAddress.new("::ffff:0:0", 443).address.should eq "::ffff:0.0.0.0"
  end

  describe "#zone_id" do
    # loopback interface "lo" is supposed to *always* be the first interface and
    # enumerated with index 1
    loopback_iface = {% if flag?(:windows) %}
                       "loopback_0"
                     {% elsif flag?(:darwin) || flag?(:bsd) || flag?(:solaris) %}
                       "lo0"
                     {% else %}
                       "lo"
                     {% end %}

    it "parses link-local IPv6 with interface scope" do
      address = Socket::IPAddress.new("fe80::3333:4444%3", 8081)
      address.address.should eq "fe80::3333:4444"
      address.zone_id.should eq 3
      address.inspect.should eq "Socket::IPAddress([fe80::3333:4444%3]:8081)"
    end

    it "looks up loopback interface index by name" do
      address = Socket::IPAddress.new("fe80::1111%#{loopback_iface}", 0)
      address.address.should eq "fe80::1111"
      address.zone_id.should eq 1
    end

    it "looks up loopback interface name by index" do
      # loopback interface "lo" is supposed to *always* be the first interface and
      # enumerated with index 1
      address = Socket::IPAddress.new("fe80::1111%#{loopback_iface}", 0)
      address.link_local_interface.should eq loopback_iface
    end

    it "fails interface name lookup for non-existent interfaces" do
      exc_suff = {% if flag?(:windows) %}
                   ""
                 {% elsif flag?(:darwin) || flag?(:bsd) %}
                   ": Device not configured"
                 {% else %}
                   ": No such device or address"
                 {% end %}
      expect_raises(Socket::Error, "Failed to look up interface name for index 333#{exc_suff}") do
        Socket::IPAddress.new("fe80::d00d:1%333", 0).link_local_interface
      end
    end

    it "interface name lookup returns nil in unsupported cases" do
      Socket::IPAddress.new("fd03::3333", 0).link_local_interface.should be_nil
      Socket::IPAddress.new("192.168.10.10", 0).link_local_interface.should be_nil
      Socket::IPAddress.new("169.254.0.3", 0).link_local_interface.should be_nil
      Socket::IPAddress.new("fe80::4545", 0).link_local_interface.should be_nil
    end

    it "fails link-local zone identifier on non-LL v6 addrs" do
      expect_raises(Socket::Error, "Zoned/scoped IPv6 addresses are only allowed for link-local (supplied 'fd00::abcd%5' is not within fe80::/10)") do
        Socket::IPAddress.new("fd00::abcd%5", 443)
      end
    end

    it "fails link-local zone identifier on v4 addrs" do
      expect_raises(Socket::Error, "Invalid IP address: 169.254.11.11%eth0") do
        Socket::IPAddress.new("169.254.11.11%eth0", 0)
      end

      expect_raises(Socket::Error, "Invalid IP address: 192.168.11.11%3") do
        Socket::IPAddress.new("192.168.11.11%3", 0)
      end
    end

    it "fails on invalid link-local zone identifier" do
      expect_raises(Socket::Error, "Invalid IPv6 link-local zone index '0' in address 'fe80::c0ff:ee%0'") do
        Socket::IPAddress.new("fe80::c0ff:ee%0", port: 0)
      end
    end

    it "fails on non-existent link-local zone interface" do
      # looking up an interface index obviously requires for said interface device to exist
      expect_raises(Socket::Error, "IPv6 link-local zone interface 'zzzzzzzzzzzzzzz' not found (in address 'fe80::0f0f:abcd%zzzzzzzzzzzzzzz')") do
        Socket::IPAddress.new("fe80::0f0f:abcd%zzzzzzzzzzzzzzz", port: 0)
      end
    end
  end

  describe ".parse" do
    it "parses IPv4" do
      address = Socket::IPAddress.parse "ip://192.168.0.1:8081"
      address.should eq Socket::IPAddress.new("192.168.0.1", 8081)
    end

    it "parses IPv6" do
      address = Socket::IPAddress.parse "ip://[::1]:8081"
      address.should eq Socket::IPAddress.new("::1", 8081)
    end

    it "fails host name" do
      expect_raises(Socket::Error, "Invalid IP address: example.com") do
        Socket::IPAddress.parse "ip://example.com:8081"
      end
    end

    it "ignores path and params" do
      address = Socket::IPAddress.parse "ip://192.168.0.1:8081/foo?bar=baz"
      address.should eq Socket::IPAddress.new("192.168.0.1", 8081)
    end

    it "fails with missing host" do
      expect_raises(Socket::Error, "Invalid IP address: missing host") do
        Socket::IPAddress.parse "ip:///path"
      end
    end

    it "fails with missing port" do
      expect_raises(Socket::Error, "Invalid IP address: missing port") do
        Socket::IPAddress.parse "ip://127.0.0.1"
      end
    end
  end

  # Tests from libc-test:
  # https://repo.or.cz/libc-test.git/blob/2113a3ed8217775797dd9a82aa420c10ef1712d5:/src/functional/inet_pton.c
  describe ".parse_v4_fields?" do
    # dotted-decimal notation
    it { Socket::IPAddress.parse_v4_fields?("0.0.0.0").should eq UInt8.static_array(0, 0, 0, 0) }
    it { Socket::IPAddress.parse_v4_fields?("127.0.0.1").should eq UInt8.static_array(127, 0, 0, 1) }
    it { Socket::IPAddress.parse_v4_fields?("10.0.128.31").should eq UInt8.static_array(10, 0, 128, 31) }
    it { Socket::IPAddress.parse_v4_fields?("255.255.255.255").should eq UInt8.static_array(255, 255, 255, 255) }

    # numbers-and-dots notation, but not dotted-decimal
    it { Socket::IPAddress.parse_v4_fields?("1.2.03.4").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2.0x33.4").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2.0XAB.4").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2.0xabcd").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.0xabcdef").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("00377.0x0ff.65534").should be_nil }

    # invalid
    it { Socket::IPAddress.parse_v4_fields?(".1.2.3").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1..2.3").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2.3.").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2.3.4.5").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2.3.a").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.256.2.3").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2.4294967296.3").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2.-4294967295.3").should be_nil }
    it { Socket::IPAddress.parse_v4_fields?("1.2. 3.4").should be_nil }
  end

  describe ".parse_v6_fields?" do
    it { Socket::IPAddress.parse_v6_fields?(":").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("::").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 0) }
    it { Socket::IPAddress.parse_v6_fields?("::1").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 1) }
    it { Socket::IPAddress.parse_v6_fields?(":::").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("192.168.1.1").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?(":192.168.1.1").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("::192.168.1.1").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0xc0a8, 0x0101) }
    it { Socket::IPAddress.parse_v6_fields?("0:0:0:0:0:0:192.168.1.1").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0xc0a8, 0x0101) }
    it { Socket::IPAddress.parse_v6_fields?("0:0::0:0:0:192.168.1.1").should eq UInt16.static_array(0, 0, 0, 0, 0, 0, 0xc0a8, 0x0101) }
    it { Socket::IPAddress.parse_v6_fields?("::012.34.56.78").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?(":ffff:192.168.1.1").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("::ffff:192.168.1.1").should eq UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0101) }
    it { Socket::IPAddress.parse_v6_fields?(".192.168.1.1").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?(":.192.168.1.1").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("a:0b:00c:000d:E:F::").should eq UInt16.static_array(0xa, 0x0b, 0x00c, 0x000d, 0xE, 0xF, 0, 0) }
    it { Socket::IPAddress.parse_v6_fields?("a:0b:00c:000d:0000e:f::").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("1:2:3:4:5:6::").should eq UInt16.static_array(1, 2, 3, 4, 5, 6, 0, 0) }
    it { Socket::IPAddress.parse_v6_fields?("1:2:3:4:5:6:7::").should eq UInt16.static_array(1, 2, 3, 4, 5, 6, 7, 0) }
    it { Socket::IPAddress.parse_v6_fields?("1:2:3:4:5:6:7:8::").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("1:2:3:4:5:6:7::9").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("::1:2:3:4:5:6").should eq UInt16.static_array(0, 0, 1, 2, 3, 4, 5, 6) }
    it { Socket::IPAddress.parse_v6_fields?("::1:2:3:4:5:6:7").should eq UInt16.static_array(0, 1, 2, 3, 4, 5, 6, 7) }
    it { Socket::IPAddress.parse_v6_fields?("::1:2:3:4:5:6:7:8").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("a:b::c:d:e:f").should eq UInt16.static_array(0xa, 0xb, 0, 0, 0xc, 0xd, 0xe, 0xf) }
    it { Socket::IPAddress.parse_v6_fields?("ffff:c0a8:5e4").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?(":ffff:c0a8:5e4").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("0:0:0:0:0:ffff:c0a8:5e4").should eq UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x5e4) }
    it { Socket::IPAddress.parse_v6_fields?("0:0:0:0:ffff:c0a8:5e4").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("0::ffff:c0a8:5e4").should eq UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x5e4) }
    it { Socket::IPAddress.parse_v6_fields?("::0::ffff:c0a8:5e4").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("c0a8").should be_nil }
    it { Socket::IPAddress.parse_v6_fields?("fe80::a:b%eth0").should eq UInt16.static_array(0xfe80, 0, 0, 0, 0, 0, 0xa, 0xb) }
    it { Socket::IPAddress.parse_v6_fields?("fe80:0:0:0:ffff:c0a8:5e4%lo").should eq UInt16.static_array(0xfe80, 0, 0, 0, 0xffff, 0xc0a8, 0x5e4, 0) }

    it { Socket::IPAddress.parse_v6_fields?("fe80::192.168.0.1%eth0").should eq UInt16.static_array(0xfe80, 0, 0, 0, 0, 0, 0xc0a8, 0x0001) }
  end

  describe ".v4" do
    it "constructs an IPv4 address" do
      Socket::IPAddress.v4(0, 0, 0, 0, port: 0).should eq Socket::IPAddress.new("0.0.0.0", 0)
      Socket::IPAddress.v4(127, 0, 0, 1, port: 1234).should eq Socket::IPAddress.new("127.0.0.1", 1234)
      Socket::IPAddress.v4(192, 168, 0, 1, port: 8081).should eq Socket::IPAddress.new("192.168.0.1", 8081)
      Socket::IPAddress.v4(255, 255, 255, 254, port: 65535).should eq Socket::IPAddress.new("255.255.255.254", 65535)
    end

    it "raises on out of bound field" do
      expect_raises(Socket::Error, "Invalid IPv4 field: 256") { Socket::IPAddress.v4(256, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: 256") { Socket::IPAddress.v4(0, 256, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: 256") { Socket::IPAddress.v4(0, 0, 256, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: 256") { Socket::IPAddress.v4(0, 0, 0, 256, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: -1") { Socket::IPAddress.v4(-1, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: -1") { Socket::IPAddress.v4(0, -1, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: -1") { Socket::IPAddress.v4(0, 0, -1, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: -1") { Socket::IPAddress.v4(0, 0, 0, -1, port: 0) }
    end

    it "raises on out of bound port number" do
      expect_raises(Socket::Error, "Invalid port number: 65536") { Socket::IPAddress.v4(0, 0, 0, 0, port: 65536) }
      expect_raises(Socket::Error, "Invalid port number: -1") { Socket::IPAddress.v4(0, 0, 0, 0, port: -1) }
    end

    it "constructs from StaticArray" do
      Socket::IPAddress.v4(UInt8.static_array(0, 0, 0, 0), 0).should eq Socket::IPAddress.new("0.0.0.0", 0)
      Socket::IPAddress.v4(UInt8.static_array(127, 0, 0, 1), 1234).should eq Socket::IPAddress.new("127.0.0.1", 1234)
      Socket::IPAddress.v4(UInt8.static_array(192, 168, 0, 1), 8081).should eq Socket::IPAddress.new("192.168.0.1", 8081)
      Socket::IPAddress.v4(UInt8.static_array(255, 255, 255, 254), 65535).should eq Socket::IPAddress.new("255.255.255.254", 65535)
    end
  end

  describe ".v6" do
    it "constructs an IPv6 address" do
      Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, 0, port: 0).should eq Socket::IPAddress.new("::", 0)
      Socket::IPAddress.v6(1, 2, 3, 4, 5, 6, 7, 8, port: 8080).should eq Socket::IPAddress.new("1:2:3:4:5:6:7:8", 8080)
      Socket::IPAddress.v6(0xfe80, 0, 0, 0, 0x4860, 0x4860, 0x4860, 0x1234, port: 55001).should eq Socket::IPAddress.new("fe80::4860:4860:4860:1234", 55001)
      Socket::IPAddress.v6(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfffe, port: 65535).should eq Socket::IPAddress.new("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", 65535)
      Socket::IPAddress.v6(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0001, port: 0).should eq Socket::IPAddress.new("::ffff:192.168.0.1", 0)
      Socket::IPAddress.v6(0xfe80, 0, 0, 0, 0x5971, 0x5971, 0x5971, 0xabcd, port: 44444, zone_id: 3).should eq Socket::IPAddress.new("fe80::5971:5971:5971:abcd%3", 44444)
    end

    it "raises on out of bound field" do
      expect_raises(Socket::Error, "Invalid IPv6 field: 65536") { Socket::IPAddress.v6(65536, 0, 0, 0, 0, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: 65536") { Socket::IPAddress.v6(0, 65536, 0, 0, 0, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: 65536") { Socket::IPAddress.v6(0, 0, 65536, 0, 0, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: 65536") { Socket::IPAddress.v6(0, 0, 0, 65536, 0, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: 65536") { Socket::IPAddress.v6(0, 0, 0, 0, 65536, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: 65536") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 65536, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: 65536") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 65536, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: 65536") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, 65536, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: -1") { Socket::IPAddress.v6(-1, 0, 0, 0, 0, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: -1") { Socket::IPAddress.v6(0, -1, 0, 0, 0, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: -1") { Socket::IPAddress.v6(0, 0, -1, 0, 0, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: -1") { Socket::IPAddress.v6(0, 0, 0, -1, 0, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: -1") { Socket::IPAddress.v6(0, 0, 0, 0, -1, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: -1") { Socket::IPAddress.v6(0, 0, 0, 0, 0, -1, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: -1") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, -1, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv6 field: -1") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, -1, port: 0) }
    end

    it "raises on out of bound port number" do
      expect_raises(Socket::Error, "Invalid port number: 65536") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, 0, port: 65536) }
      expect_raises(Socket::Error, "Invalid port number: -1") { Socket::IPAddress.v6(0, 0, 0, 0, 0, 0, 0, 0, port: -1) }
    end

    it "constructs from StaticArray" do
      Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0, 0, 0), 0).should eq Socket::IPAddress.new("::", 0)
      Socket::IPAddress.v6(UInt16.static_array(1, 2, 3, 4, 5, 6, 7, 8), 8080).should eq Socket::IPAddress.new("1:2:3:4:5:6:7:8", 8080)
      Socket::IPAddress.v6(UInt16.static_array(0xfe80, 0, 0, 0, 0x4860, 0x4860, 0x4860, 0x1234), 55001).should eq Socket::IPAddress.new("fe80::4860:4860:4860:1234", 55001)
      Socket::IPAddress.v6(UInt16.static_array(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xfffe), 65535).should eq Socket::IPAddress.new("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", 65535)
      Socket::IPAddress.v6(UInt16.static_array(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0001), 0).should eq Socket::IPAddress.new("::ffff:192.168.0.1", 0)
    end
  end

  describe ".v4_mapped_v6" do
    it "constructs an IPv4-mapped IPv6 address" do
      Socket::IPAddress.v4_mapped_v6(0, 0, 0, 0, port: 0).should eq Socket::IPAddress.new("::ffff:0.0.0.0", 0)
      Socket::IPAddress.v4_mapped_v6(127, 0, 0, 1, port: 1234).should eq Socket::IPAddress.new("::ffff:127.0.0.1", 1234)
      Socket::IPAddress.v4_mapped_v6(192, 168, 0, 1, port: 8081).should eq Socket::IPAddress.new("::ffff:192.168.0.1", 8081)
      Socket::IPAddress.v4_mapped_v6(255, 255, 255, 254, port: 65535).should eq Socket::IPAddress.new("::ffff:255.255.255.254", 65535)
    end

    it "raises on out of bound field" do
      expect_raises(Socket::Error, "Invalid IPv4 field: 256") { Socket::IPAddress.v4_mapped_v6(256, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: 256") { Socket::IPAddress.v4_mapped_v6(0, 256, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: 256") { Socket::IPAddress.v4_mapped_v6(0, 0, 256, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: 256") { Socket::IPAddress.v4_mapped_v6(0, 0, 0, 256, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: -1") { Socket::IPAddress.v4_mapped_v6(-1, 0, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: -1") { Socket::IPAddress.v4_mapped_v6(0, -1, 0, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: -1") { Socket::IPAddress.v4_mapped_v6(0, 0, -1, 0, port: 0) }
      expect_raises(Socket::Error, "Invalid IPv4 field: -1") { Socket::IPAddress.v4_mapped_v6(0, 0, 0, -1, port: 0) }
    end

    it "raises on out of bound port number" do
      expect_raises(Socket::Error, "Invalid port number: 65536") { Socket::IPAddress.v4_mapped_v6(0, 0, 0, 0, port: 65536) }
      expect_raises(Socket::Error, "Invalid port number: -1") { Socket::IPAddress.v4_mapped_v6(0, 0, 0, 0, port: -1) }
    end

    it "constructs from StaticArray" do
      Socket::IPAddress.v4_mapped_v6(UInt8.static_array(0, 0, 0, 0), 0).should eq Socket::IPAddress.new("::ffff:0.0.0.0", 0)
      Socket::IPAddress.v4_mapped_v6(UInt8.static_array(127, 0, 0, 1), 1234).should eq Socket::IPAddress.new("::ffff:127.0.0.1", 1234)
      Socket::IPAddress.v4_mapped_v6(UInt8.static_array(192, 168, 0, 1), 8081).should eq Socket::IPAddress.new("::ffff:192.168.0.1", 8081)
      Socket::IPAddress.v4_mapped_v6(UInt8.static_array(255, 255, 255, 254), 65535).should eq Socket::IPAddress.new("::ffff:255.255.255.254", 65535)
    end
  end

  it ".valid_v6?" do
    Socket::IPAddress.valid_v6?("::1").should be_true
    Socket::IPAddress.valid_v6?("x").should be_false
    Socket::IPAddress.valid_v6?("127.0.0.1").should be_false
  end

  it ".valid_v4?" do
    Socket::IPAddress.valid_v4?("127.0.0.1").should be_true
    Socket::IPAddress.valid_v4?("::1").should be_false
    Socket::IPAddress.valid_v4?("x").should be_false
  end

  it ".valid?" do
    Socket::IPAddress.valid?("127.0.0.1").should be_true
    Socket::IPAddress.valid?("::1").should be_true
    Socket::IPAddress.valid?("x").should be_false
  end

  it "#loopback?" do
    Socket::IPAddress.new("127.0.0.1", 0).loopback?.should be_true
    Socket::IPAddress.new("127.255.255.254", 0).loopback?.should be_true
    Socket::IPAddress.new("128.0.0.1", 0).loopback?.should be_false
    Socket::IPAddress.new("0.0.0.0", 0).loopback?.should be_false
    Socket::IPAddress.new("::1", 0).loopback?.should be_true
    Socket::IPAddress.new("0000:0000:0000:0000:0000:0000:0000:0001", 0).loopback?.should be_true
    Socket::IPAddress.new("::2", 0).loopback?.should be_false
    Socket::IPAddress.new(Socket::IPAddress::LOOPBACK, 0).loopback?.should be_true
    Socket::IPAddress.new(Socket::IPAddress::LOOPBACK6, 0).loopback?.should be_true
    Socket::IPAddress.new("::ffff:127.0.0.1", 0).loopback?.should be_true
    Socket::IPAddress.new("::ffff:127.0.1.1", 0).loopback?.should be_true
    Socket::IPAddress.new("::ffff:1.0.0.1", 0).loopback?.should be_false
  end

  it "#unspecified?" do
    Socket::IPAddress.new("0.0.0.0", 0).unspecified?.should be_true
    Socket::IPAddress.new("127.0.0.1", 0).unspecified?.should be_false
    Socket::IPAddress.new("::", 0).unspecified?.should be_true
    Socket::IPAddress.new("0000:0000:0000:0000:0000:0000:0000:0000", 0).unspecified?.should be_true
    Socket::IPAddress.new(Socket::IPAddress::UNSPECIFIED, 0).unspecified?.should be_true
    Socket::IPAddress.new(Socket::IPAddress::UNSPECIFIED6, 0).unspecified?.should be_true
  end

  it ".valid_port?" do
    Socket::IPAddress.valid_port?(0).should be_true
    Socket::IPAddress.valid_port?(80).should be_true
    Socket::IPAddress.valid_port?(65_535).should be_true

    Socket::IPAddress.valid_port?(-1).should be_false
    Socket::IPAddress.valid_port?(65_536).should be_false
  end

  it "#private?" do
    Socket::IPAddress.new("192.168.0.1", 0).private?.should be_true
    Socket::IPAddress.new("192.100.0.1", 0).private?.should be_false
    Socket::IPAddress.new("172.16.0.1", 0).private?.should be_true
    Socket::IPAddress.new("172.10.0.1", 0).private?.should be_false
    Socket::IPAddress.new("10.0.0.1", 0).private?.should be_true
    Socket::IPAddress.new("1.1.1.1", 0).private?.should be_false
    Socket::IPAddress.new("fd00::1", 0).private?.should be_true
    Socket::IPAddress.new("fb00::1", 0).private?.should be_false
    Socket::IPAddress.new("2001:4860:4860::8888", 0).private?.should be_false
  end

  it "#link_local?" do
    Socket::IPAddress.new("0.0.0.0", 0).link_local?.should be_false
    Socket::IPAddress.new("127.0.0.1", 0).link_local?.should be_false
    Socket::IPAddress.new("10.0.0.0", 0).link_local?.should be_false
    Socket::IPAddress.new("172.16.0.0", 0).link_local?.should be_false
    Socket::IPAddress.new("192.168.0.0", 0).link_local?.should be_false

    Socket::IPAddress.new("169.254.1.1", 0).link_local?.should be_true
    Socket::IPAddress.new("169.254.254.255", 0).link_local?.should be_true

    Socket::IPAddress.new("::1", 0).link_local?.should be_false
    Socket::IPAddress.new("::", 0).link_local?.should be_false
    Socket::IPAddress.new("fb84:8bf7:e905::1", 0).link_local?.should be_false

    Socket::IPAddress.new("fe80::4860:4860:4860:1234", 0).link_local?.should be_true
  end

  it "#==" do
    Socket::IPAddress.new("127.0.0.1", 8080).should eq Socket::IPAddress.new("127.0.0.1", 8080)
    Socket::IPAddress.new("127.0.0.1", 8080).hash.should eq Socket::IPAddress.new("127.0.0.1", 8080).hash

    Socket::IPAddress.new("127.0.0.1", 8080).should_not eq Socket::IPAddress.new("127.0.0.1", 8081)
    Socket::IPAddress.new("127.0.0.1", 8080).hash.should_not eq Socket::IPAddress.new("127.0.0.1", 8081).hash

    Socket::IPAddress.new("127.0.0.1", 8080).should_not eq Socket::IPAddress.new("127.0.0.2", 8080)
    Socket::IPAddress.new("127.0.0.1", 8080).hash.should_not eq Socket::IPAddress.new("127.0.0.2", 8080).hash
  end
end

{% if flag?(:unix) %}
  describe Socket::UNIXAddress do
    it "transforms into a C struct and back" do
      path = "unix_address.sock"

      addr1 = Socket::UNIXAddress.new(path)
      addr2 = Socket::UNIXAddress.from(addr1.to_unsafe, addr1.size)

      addr2.family.should eq(addr1.family)
      addr2.path.should eq(addr1.path)
      addr2.to_s.should eq(path)
      addr2 = Socket::UNIXAddress.from(addr1.to_unsafe)
    end

    it "raises when path is too long" do
      path = "unix_address-too-long-#{("a" * 2048)}.sock"

      expect_raises(ArgumentError, "Path size exceeds the maximum size") do
        Socket::UNIXAddress.new(path)
      end
    end

    it "to_s" do
      Socket::UNIXAddress.new("some_path").to_s.should eq("some_path")
    end

    it "#==" do
      Socket::UNIXAddress.new("some_path").should eq Socket::UNIXAddress.new("some_path")
      Socket::UNIXAddress.new("some_path").hash.should eq Socket::UNIXAddress.new("some_path").hash

      Socket::UNIXAddress.new("some_path").should_not eq Socket::UNIXAddress.new("other_path")
      Socket::UNIXAddress.new("some_path").hash.should_not eq Socket::UNIXAddress.new("other_path").hash
    end

    it "accepts `Path` input" do
      Socket::UNIXAddress.new(Path.new("some_path")).should eq Socket::UNIXAddress.new("some_path")
    end

    describe ".parse" do
      it "parses relative" do
        address = Socket::UNIXAddress.parse "unix://foo.sock"
        address.should eq Socket::UNIXAddress.new("foo.sock")
      end

      it "parses relative subpath" do
        address = Socket::UNIXAddress.parse "unix://foo/bar.sock"
        address.should eq Socket::UNIXAddress.new("foo/bar.sock")
      end

      it "parses relative dot" do
        address = Socket::UNIXAddress.parse "unix://./bar.sock"
        address.should eq Socket::UNIXAddress.new("./bar.sock")
      end

      it "relative with" do
        address = Socket::UNIXAddress.parse "unix://foo:21/bar.sock"
        address.should eq Socket::UNIXAddress.new("foo:21/bar.sock")
      end

      it "parses absolute" do
        address = Socket::UNIXAddress.parse "unix:///foo.sock"
        address.should eq Socket::UNIXAddress.new("/foo.sock")
      end

      it "ignores params" do
        address = Socket::UNIXAddress.parse "unix:///foo.sock?bar=baz"
        address.should eq Socket::UNIXAddress.new("/foo.sock")
      end

      it "fails with missing path" do
        expect_raises(Socket::Error, "Invalid UNIX address: missing path") do
          Socket::UNIXAddress.parse "unix://?foo=bar"
        end
      end
    end
  end
{% end %}

describe Socket do
  # Most of the specs are moved to `.parse_v4_fields?` and `.parse_v6_fields?`,
  # which are implemented in pure Crystal; the remaining ones here are test
  # cases that were once known to break on certain platforms when `Socket.ip?`
  # was still using the system `inet_pton`
  it ".ip?" do
    Socket.ip?("1.2.03.4").should be_false
    Socket.ip?("::012.34.56.78").should be_false
    Socket.ip?("a:0b:00c:000d:0000e:f::").should be_false
    Socket.ip?("::1:2:3:4:5:6:7").should be_true
  end

  it "==" do
    a = Socket::IPAddress.new("127.0.0.1", 8080)
    b = Socket::UNIXAddress.new("some_path")
    c = "some_path"
    (a == a).should be_true
    (b == b).should be_true
    (a == b).should be_false
    (a == c).should be_false
    (b == c).should be_false
  end
end
