1defmodule MsgpaxTest do 2 use Msgpax.Case, async: true 3 4 doctest Msgpax 5 6 alias Msgpax.PackError 7 alias Msgpax.UnpackError 8 9 defmodule User do 10 @derive [Msgpax.Packer] 11 defstruct [:name] 12 end 13 14 defmodule UserWithAge do 15 @derive [{Msgpax.Packer, fields: [:name]}] 16 defstruct [:name, :age] 17 end 18 19 defmodule UserAllFields do 20 @derive [{Msgpax.Packer, include_struct_field: true}] 21 defstruct [:name] 22 end 23 24 defmodule UserDerivingStructField do 25 @derive [{Msgpax.Packer, fields: [:name, :__struct__]}] 26 defstruct [:name] 27 end 28 29 test "fixstring" do 30 assert_format build_string(0), [160] 31 assert_format build_string(31), [191] 32 end 33 34 test "string 8" do 35 assert_format build_string(32), [217, 32] 36 assert_format build_string(255), [217, 255] 37 end 38 39 test "string 16" do 40 assert_format build_string(0x100), [218, 0x100::16] 41 assert_format build_string(0xFFFF), [218, 0xFFFF::16] 42 end 43 44 test "string 32" do 45 assert_format build_string(0x10000), [219, 0x10000::32] 46 end 47 48 test "binary 8" do 49 assert_format build_bytes(1), [0xC4, 1], build_string(1) 50 assert_format build_bytes(255), [0xC4, 255], build_string(255) 51 52 assert_format build_bytes(1), [0xC4, 1], {build_bytes(1), [binary: true]} 53 assert_format build_bytes(255), [0xC4, 255], {build_bytes(255), [binary: true]} 54 end 55 56 test "binary 16" do 57 assert_format build_bytes(0x100), [0xC5, 0x100::16], build_string(0x100) 58 assert_format build_bytes(0xFFFF), [0xC5, 0xFFFF::16], build_string(0xFFFF) 59 end 60 61 test "binary 32" do 62 assert_format build_bytes(0x10000), [0xC6, 0x10000::32], build_string(0x10000) 63 end 64 65 test "fixarray" do 66 assert_format build_list(0), [144] 67 assert_format build_list(15), [159] 68 end 69 70 test "array 16" do 71 assert_format build_list(16), [220, 16::16] 72 assert_format build_list(0xFFFF), [220, 0xFFFF::16] 73 end 74 75 test "array 32" do 76 assert_format build_list(0x10000), [221, 0x10000::32] 77 end 78 79 test "fixmap" do 80 assert_format build_map(0), [128] 81 assert_format build_map(15), [143] 82 end 83 84 test "map 16" do 85 assert_format build_map(16), [222, 16::16] 86 assert_format build_map(0xFFFF), [222, 0xFFFF::16] 87 end 88 89 test "map 32" do 90 assert_format build_map(0x10000), [223, 0x10000::32] 91 end 92 93 test "booleans" do 94 assert_format false, [194] 95 assert_format true, [195] 96 end 97 98 test "nil" do 99 assert_format nil, [192] 100 end 101 102 test "atoms" do 103 assert_format :ok, [162], "ok" 104 assert_format Atom, [171], "Elixir.Atom" 105 end 106 107 test "float" do 108 assert_format 42.1, [203] 109 end 110 111 test "positive fixint" do 112 assert_format 0, [0] 113 assert_format 127, [127] 114 end 115 116 test "uint 8" do 117 assert_format 128, [204] 118 assert_format 255, [204] 119 end 120 121 test "uint 16" do 122 assert_format 256, [205] 123 assert_format 65535, [205] 124 end 125 126 test "uint 32" do 127 assert_format 65536, [206] 128 assert_format 4294967295, [206] 129 end 130 131 test "uint 64" do 132 assert_format 4294967296, [207] 133 end 134 135 test "negative fixint" do 136 assert_format -1, [255] 137 assert_format -32, [224] 138 end 139 140 test "int 8" do 141 assert_format -33, [208] 142 assert_format -128, [208] 143 end 144 145 test "int 16" do 146 assert_format -129, [209] 147 assert_format -32768, [209] 148 end 149 150 test "int 32" do 151 assert_format -32769, [210] 152 assert_format -2147483648, [210] 153 end 154 155 test "int 64" do 156 assert_format -2147483649, [211] 157 end 158 159 test "complex structures" do 160 data = %{[[123, "foo"], [true]] => [nil, -45, [%{[] => 10.0}]]} 161 assert_format data, [], data 162 assert_format [data], [], [data] 163 end 164 165 test "bitstring" do 166 assert Msgpax.pack([42, <<5::3>>]) == {:error, %PackError{reason: {:not_encodable, <<5::3>>}}} 167 end 168 169 test "too big data" do 170 assert Msgpax.pack([true, -9223372036854775809]) == {:error, %PackError{reason: {:too_big, -9223372036854775809}}} 171 end 172 173 test "pack/2 with the :iodata option" do 174 assert Msgpax.pack([], iodata: true) == {:ok, [144]} 175 assert Msgpax.pack([], iodata: false) == {:ok, <<144>>} 176 assert Msgpax.pack([42, <<5::3>>], iodata: false) == {:error, %PackError{reason: {:not_encodable, <<5::3>>}}} 177 end 178 179 test "pack!/2 with the :iodata option" do 180 assert Msgpax.pack!([], iodata: true) == [144] 181 assert Msgpax.pack!([], iodata: false) == <<144>> 182 assert_raise Msgpax.PackError, fn -> 183 Msgpax.pack!([42, <<5::3>>], iodata: false) 184 end 185 end 186 187 test "excess bytes" do 188 assert Msgpax.unpack(<<255, 1, 2>>) == {:error, %UnpackError{reason: {:excess_bytes, <<1, 2>>}}} 189 end 190 191 test "invalid format" do 192 assert Msgpax.unpack(<<145, 191>>) == {:error, %UnpackError{reason: {:invalid_format, 191}}} 193 assert Msgpax.unpack(<<193, 1>>) == {:error, %UnpackError{reason: {:invalid_format, 193}}} 194 end 195 196 test "incomplete binary" do 197 assert Msgpax.unpack(<<147, 1, 2>>) == {:error, %UnpackError{reason: :incomplete}} 198 assert Msgpax.unpack(<<5::3>>) == {:error, %UnpackError{reason: :incomplete}} 199 end 200 201 test "unpack_slice/1" do 202 assert Msgpax.unpack_slice(<<255, 1>>) == {:ok, -1, <<1>>} 203 assert Msgpax.unpack_slice(<<5::3>>) == {:error, %UnpackError{reason: :incomplete}} 204 end 205 206 test "deriving" do 207 assert Msgpax.pack!(%User{name: "Lex"}) == Msgpax.pack!(%{name: "Lex"}) 208 209 assert_raise Protocol.UndefinedError, fn -> 210 Msgpax.pack!(%URI{}) 211 end 212 213 assert Msgpax.pack!(%UserWithAge{name: "Luke", age: 9}) == Msgpax.pack!(%{name: "Luke"}) 214 215 expected = Msgpax.pack!(%{"__struct__" => UserAllFields, name: "Francine"}) 216 assert Msgpax.pack!(%UserAllFields{name: "Francine"}) == expected 217 218 expected = Msgpax.pack!(%{"__struct__" => UserDerivingStructField, name: "Juri"}) 219 assert Msgpax.pack!(%UserDerivingStructField{name: "Juri"}) == expected 220 end 221 222 test "timestamp ext" do 223 string = 224 if Version.match?(System.version(), "<= 1.5.1") do 225 "0001-01-01T00:00:00.000000Z" 226 else 227 "0001-01-01T00:00:00.000001Z" 228 end 229 {:ok, datetime, 0} = DateTime.from_iso8601(string) 230 assert_format datetime, [] 231 232 {:ok, datetime, 0} = DateTime.from_iso8601("1970-01-01T00:00:00Z") 233 assert_format datetime, [] 234 235 datetime = DateTime.utc_now() 236 assert_format datetime, [] 237 238 string = 239 if Version.match?(System.version(), "<= 1.5.2") do 240 "9999-12-31T23:59:59.0000000Z" 241 else 242 "9999-12-31T23:59:59.9999999Z" 243 end 244 {:ok, datetime, 0} = DateTime.from_iso8601(string) 245 assert_format datetime, [] 246 end 247 248 defp build_string(length) do 249 String.duplicate(".", length) 250 end 251 252 defp build_list(length) do 253 List.duplicate(nil, length) 254 end 255 256 defp build_bytes(size) do 257 size |> build_string() |> Msgpax.Bin.new() 258 end 259 260 defp build_map(0) do 261 %{} 262 end 263 264 defp build_map(size) do 265 {0, true} 266 |> Stream.iterate(fn {index, value} -> {index + 1, value} end) 267 |> Enum.take(size) 268 |> Enum.into(%{}) 269 end 270end 271