From 29a475d67924eacf4d7c1a0bea6d46fec094cacc Mon Sep 17 00:00:00 2001 From: Devin Alexander Torres Date: Thu, 22 Jul 2021 15:14:14 -0500 Subject: [PATCH] Add GitHub actions --- .circleci/config.yml | 130 ----------------------------------- .dialyzer_ignore.exs | 4 +- .github/workflows/ci.yml | 61 ++++++++++++++++ .travis.yml | 29 -------- README.md | 8 +-- mix.exs | 2 +- mix.lock | 2 +- test/poison/encoder_test.exs | 106 +++++++++++++--------------- test/poison/parser_test.exs | 42 +++-------- test/test_helper.exs | 48 +++++++++++++ 10 files changed, 174 insertions(+), 258 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 7af276e9..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,130 +0,0 @@ -version: 2.1 - -executors: - elixir: - docker: - - image: cimg/elixir:1.12.2 - environment: - MIX_ENV: test - -jobs: - build: - executor: elixir - steps: - - checkout - - - run: elixir -e 'IO.binwrite("#{System.version}-otp-#{:erlang.system_info(:otp_release)}")' | tee -i ~/.elixir-version - - restore_cache: - keys: - - v1-build-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - - v1-build-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}- - - v1-build-cache-{{ checksum "~/.elixir-version" }}- - - - run: mix local.hex --force --if-missing - - run: mix local.rebar --force --if-missing - - - run: mix deps.get - - run: mix deps.compile - - run: mix compile - - - save_cache: - key: v1-build-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - paths: - - ~/.mix - - deps - - _build - - - persist_to_workspace: - root: ~/ - paths: - - .elixir-version - - .mix - - .hex - - format: - executor: elixir - steps: - - attach_workspace: - at: ~/ - - checkout - - restore_cache: - key: v1-build-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - - run: mix format --check-formatted --dry-run - - credo: - executor: elixir - steps: - - attach_workspace: - at: ~/ - - checkout - - restore_cache: - key: v1-build-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - - run: mix credo - - dialyzer: - executor: elixir - steps: - - attach_workspace: - at: ~/ - - checkout - - restore_cache: - keys: - - v1-plt-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - - v1-plt-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}- - - v1-build-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - - run: mix dialyzer --plt - - save_cache: - key: v1-plt-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - paths: - - ~/.mix - - deps - - _build - - run: mix dialyzer --no-check - - test: - executor: elixir - steps: - - attach_workspace: - at: ~/ - - checkout - - run: git submodule update --init - - restore_cache: - key: v1-build-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - - run: mix coveralls.html - - store_test_results: - path: _build/test/lib/poison - - store_artifacts: - path: cover - - docs: - executor: elixir - steps: - - attach_workspace: - at: ~/ - - checkout - - restore_cache: - key: v1-build-cache-{{ checksum "~/.elixir-version" }}-{{ checksum "mix.lock" }}-{{ .Revision }} - - run: mix docs - - store_artifacts: - path: doc - -workflows: - version: 2 - build_and_test: - jobs: - - build - - format: - requires: - - build - - credo: - requires: - - build - - dialyzer: - requires: - - build - - test: - requires: - - build - - docs: - requires: - - build diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs index 47ec0c27..23fc3f6f 100644 --- a/.dialyzer_ignore.exs +++ b/.dialyzer_ignore.exs @@ -1,5 +1,5 @@ [ - {"lib/poison/encoder.ex", :unknown_function, 0}, - {"lib/poison/decoder.ex", :unknown_function, 0}, + {"lib/poison/encoder.ex", :unknown_function, 80}, + {"lib/poison/decoder.ex", :unknown_function, 106}, {"lib/poison/parser.ex", :improper_list_constr} ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..9e6a1bf6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: ci + +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + MIX_ENV: test + +jobs: + test: + runs-on: ubuntu-latest + name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}} + strategy: + matrix: + otp: + - '24.0' + - '23.3' + - '23.2' + - '23.1' + - '23.0' + elixir: + - '1.12.2' + - '1.12.1' + - '1.12.0' + - '1.11.4' + - '1.11.3' + - '1.11.2' + - '1.11.1' + - '1.11.0' + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: true + + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{matrix.otp}} + elixir-version: ${{matrix.elixir}} + + - uses: actions/cache@v2 + id: mix-cache + with: + path: | + _build + deps + vendor + key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('mix.lock') }} + + - if: steps.mix-cache.outputs.cache-hit != 'true' + run: mix deps.get + + - run: mix coveralls.github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b5e28425..00000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -dist: focal -sudo: false -language: elixir -elixir: - - 1.12.2 - - 1.12.1 - - 1.12.0 - - 1.11.4 - - 1.11.3 - - 1.11.2 - - 1.11.1 - - 1.11.0 -otp_release: - - 24.0 - - 23.3 - - 23.2 - - 23.1 - - 23.0 -before_script: git submodule update --init -matrix: - include: - - install: - - mix local.rebar --force - - mix local.hex --force - script: - - mix format --check-formatted --dry-run - - mix credo --strict - - secure: fpg2leA+GSEcbW+B9lkUPFHAsPoiq9VIF48izB4anR65TZgPIHoy7W04Ly+cHSDuPYPxxtCuZW5YU5ZBi3GAXNLsKZG3uTaoLQj+atZsDdyZNW7Z7Df2uVm3eIK7wC7XojalNtShYx56xTC19xrH01n6VouAcbgJ3TIfxQCraDk= - script: mix coveralls.travis diff --git a/README.md b/README.md index d473a53e..93acfe69 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Poison -[![Build Status](https://travis-ci.org/devinus/poison.svg?branch=master)](https://travis-ci.org/devinus/poison) -[![Coverage Status](https://coveralls.io/repos/github/devinus/poison/badge.svg?branch=master)](https://coveralls.io/github/devinus/poison?branch=master) +[![Build Status](https://img.shields.io/github/workflow/status/devinus/poison/ci/master)](https://github.com/devinus/poison/actions/workflows/ci.yml) +[![Coverage Status](https://img.shields.io/coveralls/github/devinus/poison/master)](https://coveralls.io/github/devinus/poison?branch=master) [![Hex.pm Version](https://img.shields.io/hexpm/v/poison.svg?style=flat-square)](https://hex.pm/packages/poison) [![Hex.pm Download Total](https://img.shields.io/hexpm/dt/poison.svg?style=flat-square)](https://hex.pm/packages/poison) @@ -160,10 +160,10 @@ $ MIX_ENV=bench mix run bench/run.exs ### Current Benchmarks -As of 2020-06-25: +As of 2021-07-22: - Amazon EC2 c5.2xlarge instance running Ubuntu Server 20.04: - https://gist.github.com/devinus/c82c2f6eaa22456e7ff0f5705466b1de + https://gist.github.com/devinus/f56cff9e5a0aa9de9215cf33212085f6 ## License diff --git a/mix.exs b/mix.exs index b982ca2e..b158d8dc 100644 --- a/mix.exs +++ b/mix.exs @@ -38,7 +38,7 @@ defmodule Poison.Mixfile do "coveralls.detail": :test, "coveralls.html": :test, "coveralls.post": :test, - "coveralls.travis": :test + "coveralls.github": :test ] ] end diff --git a/mix.lock b/mix.lock index cce0b59b..cddcb74b 100644 --- a/mix.lock +++ b/mix.lock @@ -11,7 +11,7 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.25.0", "4070a254664ee5495c2f7cce87c2f43064a8752f7976f2de4937b65871b05223", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2d90883bd4f3d826af0bde7fea733a4c20adba1c79158e2330f7465821c8949b"}, - "excoveralls": {:hex, :excoveralls, "0.14.1", "14140e4ef343f2af2de33d35268c77bc7983d7824cb945e6c2af54235bc2e61f", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4a588f9f8cf9dc140cc1f3d0ea4d849b2f76d5d8bee66b73c304bb3d3689c8b0"}, + "excoveralls": {:hex, :excoveralls, "0.14.2", "f9f5fd0004d7bbeaa28ea9606251bb643c313c3d60710bad1f5809c845b748f0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "ca6fd358621cb4d29311b29d4732c4d47dac70e622850979bc54ed9a3e50f3e1"}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"}, diff --git a/test/poison/encoder_test.exs b/test/poison/encoder_test.exs index ca9f5156..6295fda0 100644 --- a/test/poison/encoder_test.exs +++ b/test/poison/encoder_test.exs @@ -2,6 +2,8 @@ defmodule Poison.EncoderTest do use ExUnit.Case, async: true use ExUnitProperties + import Poison.TestGenerators + alias Poison.{EncodeError, Encoder} test "Atom" do @@ -12,11 +14,12 @@ defmodule Poison.EncoderTest do end property "Atom" do - check all str <- string(:printable), - str not in ~w(nil true false), - value = String.to_atom(str) do - # credo:disable-for-previous-line Credo.Check.Warning.UnsafeToAtom - assert to_json(value) == ~s("#{value}") + check all( + str <- filter(json_string(max_length: 255), &(&1 not in ~w(nil true false))), + # credo:disable-for-next-line Credo.Check.Warning.UnsafeToAtom + value = String.to_atom(str) + ) do + assert to_json(value) == ~s("#{escape_string(str)}") end end @@ -25,7 +28,7 @@ defmodule Poison.EncoderTest do end property "Integer" do - check all value <- integer() do + check all(value <- integer()) do assert to_json(value) == to_string(value) end end @@ -36,7 +39,7 @@ defmodule Poison.EncoderTest do end property "Float" do - check all value <- float() do + check all(value <- float()) do assert to_json(value) == to_string(value) end end @@ -65,20 +68,23 @@ defmodule Poison.EncoderTest do end property "BitString" do - check all value <- string(:printable) do - assert to_json(value) == ~s("#{value}") + check all(value <- json_string()) do + assert to_json(value) == ~s("#{escape_string(value)}") end - check all str <- string(Enum.concat(0xA0..0xD7FF, 0xE000..0x10000)), - str != "", - elem <- member_of(String.codepoints(str)), - <> = elem do + check all( + str <- string(Enum.concat(0xA0..0xD7FF, 0xE000..0x10000), min_length: 1), + elem <- member_of(String.codepoints(str)), + <> = elem + ) do seq = codepoint |> Integer.to_string(16) |> String.pad_leading(4, "0") assert to_json(<>, escape: :unicode) == ~s("\\u#{seq}") end - check all hi <- integer(0xD800..0xDBFF), - lo <- integer(0xDC00..0xDFFF) do + check all( + hi <- integer(0xD800..0xDBFF), + lo <- integer(0xDC00..0xDFFF) + ) do seq1 = hi |> Integer.to_string(16) |> String.pad_leading(4, "0") seq2 = lo |> Integer.to_string(16) |> String.pad_leading(4, "0") <> = <> @@ -88,8 +94,7 @@ defmodule Poison.EncoderTest do end property "List" do - check all value <- list_of(gen_value()), - value != [] do + check all(value <- json_list(min_length: 1)) do assert String.match?(to_json(value), ~r/^\[.*\]$/) end end @@ -117,8 +122,7 @@ defmodule Poison.EncoderTest do end property "Map" do - check all value <- map_of(string(:printable), gen_value()), - value != %{} do + check all(value <- json_map(min_length: 1)) do assert String.match?(to_json(value), ~r/^{.*}$/) end end @@ -226,6 +230,22 @@ defmodule Poison.EncoderTest do assert to_json(datetime) == ~s("2000-01-01T12:13:14.050Z") end + test "URI" do + uri = URI.parse("https://devinus.io") + assert to_json(uri) == ~s("https://devinus.io") + end + + test "Decimal" do + decimal = Decimal.new("99.9") + assert to_json(decimal) == "99.9" + end + + property "Decimal" do + check all(value <- map(float(), &Decimal.from_float/1)) do + assert to_json(value) == to_string(value) + end + end + defmodule Derived do @derive [Poison.Encoder] defstruct name: "" @@ -275,14 +295,16 @@ defmodule Poison.EncoderTest do end property "complex nested input" do - check all value <- gen_complex_value(), - options <- - optional_map(%{ - escape: one_of([:unicode, :javascript, :html_safe]), - pretty: boolean(), - indent: positive_integer(), - offset: positive_integer() - }) do + check all( + value <- json_complex_value(), + options <- + optional_map(%{ + escape: one_of([:unicode, :javascript, :html_safe]), + pretty: boolean(), + indent: positive_integer(), + offset: positive_integer() + }) + ) do assert to_json(value, options) != "" end end @@ -292,34 +314,4 @@ defmodule Poison.EncoderTest do |> Encoder.encode(Map.new(options)) |> IO.iodata_to_binary() end - - defp gen_string do - string([0x0..0xD7FF, 0xE000..0xFFFF]) - end - - defp gen_value do - one_of([ - constant(nil), - boolean(), - integer(), - float(), - gen_string(), - map(float(), &Decimal.from_float/1) - ]) - end - - defp gen_complex_value do - one_of([ - gen_value(), - list_of(gen_value()), - map_of( - gen_string(), - one_of([ - gen_value(), - list_of(gen_value()), - map_of(gen_string(), gen_value()) - ]) - ) - ]) - end end diff --git a/test/poison/parser_test.exs b/test/poison/parser_test.exs index 73c35997..a2c5bfef 100644 --- a/test/poison/parser_test.exs +++ b/test/poison/parser_test.exs @@ -2,7 +2,7 @@ defmodule Poison.ParserTest do use ExUnit.Case, async: true use ExUnitProperties - import Poison.Parser + import Poison.{TestGenerators, Parser} alias Poison.ParseError test "numbers" do @@ -75,6 +75,10 @@ defmodule Poison.ParserTest do check all(value <- float()) do assert parse!(Float.to_string(value)) == value end + + check all(value <- map(float(), &Float.to_string/1)) do + assert Decimal.equal?(parse!(value, %{decimal: true}), value) + end end test "strings" do @@ -132,8 +136,8 @@ defmodule Poison.ParserTest do end property "strings" do - check all(str <- string(:printable)) do - assert parse!(~s("#{str}")) == str + check all(value <- json_string()) do + assert parse!(~s("#{escape_string(value)}")) == value end check all(value <- member_of(Enum.concat(0x0..0xD7FF, 0xE000..0xFFFF))) do @@ -242,7 +246,7 @@ defmodule Poison.ParserTest do property "complex nested input" do check all( - value <- gen_complex_value(), + value <- json_complex_value(), options <- optional_map(%{ keys: one_of([:atoms!, :atoms]), @@ -281,34 +285,4 @@ defmodule Poison.ParserTest do exception -> {:error, exception} end - - defp gen_string do - string([0x0..0xD7FF, 0xE000..0xFFFF]) - end - - defp gen_value do - one_of([ - constant(nil), - boolean(), - integer(), - float(), - gen_string(), - map(float(), &Decimal.from_float/1) - ]) - end - - defp gen_complex_value do - one_of([ - gen_value(), - list_of(gen_value()), - map_of( - gen_string(), - one_of([ - gen_value(), - list_of(gen_value()), - map_of(gen_string(), gen_value()) - ]) - ) - ]) - end end diff --git a/test/test_helper.exs b/test/test_helper.exs index 142f0fb4..82aa055b 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,2 +1,50 @@ +defmodule Poison.TestGenerators do + use ExUnitProperties + + def json_string(options \\ []) do + string( + ~c(\b\t\n\f\r) ++ [0x20..0x7E, 0xA0..0xD7FF, 0xE000..0xFFFD, 0x10000..0x10FFFF], + options + ) + end + + def json_value do + one_of([ + constant(nil), + boolean(), + integer(), + float(), + json_string() + ]) + end + + def json_list(options \\ []) do + list_of(json_value(), options) + end + + def json_map(options \\ []) do + map_of(json_string(), json_value(), options) + end + + def json_complex_value do + one_of([ + json_value(), + json_list(), + map_of( + json_string(), + one_of([ + json_value(), + json_list(), + json_map() + ]) + ) + ]) + end + + def escape_string(str) do + str |> String.replace("\\", "\\\\") |> String.replace("\"", "\\\"") + end +end + ExUnit.configure(formatters: [ExUnit.CLIFormatter, JUnitFormatter]) ExUnit.start()