diff --git a/.builds/alpine.yaml b/.builds/alpine.yaml new file mode 100644 index 0000000..0c8a630 --- /dev/null +++ b/.builds/alpine.yaml @@ -0,0 +1,20 @@ +image: alpine/latest +packages: + - g++ + - make + - elixir + - file + - file-dev +sources: + - https://git.sr.ht/~href/gen_magic +tasks: + - setup: | + mix local.hex --force + - build: | + cd gen_magic + mix deps.get + MIX_ENV=test mix compile + - test: | + cd gen_magic + mix test + diff --git a/.builds/archlinux.yaml b/.builds/archlinux.yaml new file mode 100644 index 0000000..4dc0c26 --- /dev/null +++ b/.builds/archlinux.yaml @@ -0,0 +1,17 @@ +image: archlinux +packages: + - elixir + - file +sources: + - https://git.sr.ht/~href/gen_magic +tasks: + - setup: | + mix local.hex --force + - build: | + cd gen_magic + mix deps.get + MIX_ENV=test mix compile + - test: | + cd gen_magic + mix test + diff --git a/.builds/debian-oldstable.yaml b/.builds/debian-oldstable.yaml new file mode 100644 index 0000000..915f1a2 --- /dev/null +++ b/.builds/debian-oldstable.yaml @@ -0,0 +1,20 @@ +image: debian/oldstable +packages: + - build-essential + - erlang + - erlang-dev + - elixir + - libmagic-dev +sources: + - https://git.sr.ht/~href/gen_magic +tasks: + - setup: | + mix local.hex --force + - build: | + cd gen_magic + mix deps.get + MIX_ENV=test mix compile + - test: | + cd gen_magic + mix test + diff --git a/.builds/debian-stable.yaml b/.builds/debian-stable.yaml new file mode 100644 index 0000000..d6bdbe2 --- /dev/null +++ b/.builds/debian-stable.yaml @@ -0,0 +1,20 @@ +image: debian/stable +packages: + - build-essential + - erlang + - erlang-dev + - elixir + - libmagic-dev +sources: + - https://git.sr.ht/~href/gen_magic +tasks: + - setup: | + mix local.hex --force + - build: | + cd gen_magic + mix deps.get + MIX_ENV=test mix compile + - test: | + cd gen_magic + mix test + diff --git a/.builds/debian-testing.yaml b/.builds/debian-testing.yaml new file mode 100644 index 0000000..bda46e4 --- /dev/null +++ b/.builds/debian-testing.yaml @@ -0,0 +1,20 @@ +image: debian/testing +packages: + - build-essential + - erlang + - erlang-dev + - elixir + - libmagic-dev +sources: + - https://git.sr.ht/~hrefhref/gen_magic +tasks: + - setup: | + mix local.hex --force + - build: | + cd gen_magic + mix deps.get + MIX_ENV=test mix compile + - test: | + cd gen_magic + mix test + diff --git a/.builds/fedora-latest.yaml b/.builds/fedora-latest.yaml new file mode 100644 index 0000000..ba87865 --- /dev/null +++ b/.builds/fedora-latest.yaml @@ -0,0 +1,22 @@ +image: fedora/latest +packages: + - make + - gcc + - kernel-devel + - erlang + - elixir + - file-devel +sources: + - https://git.sr.ht/~href/gen_magic +tasks: + - setup: | + sudo dnf -y group install 'Development Tools' + mix local.hex --force + - build: | + cd gen_magic + mix deps.get + MIX_ENV=test mix compile + - test: | + cd gen_magic + mix test + diff --git a/.builds/freebsd.yaml b/.builds/freebsd.yaml new file mode 100644 index 0000000..3a78947 --- /dev/null +++ b/.builds/freebsd.yaml @@ -0,0 +1,17 @@ +image: freebsd/latest +packages: + - elixir + - gmake +sources: + - https://git.sr.ht/~href/gen_magic +tasks: + - setup: | + mix local.hex --force + - build: | + cd gen_magic + mix deps.get + MIX_ENV=test mix compile + - test: | + cd gen_magic + mix test + diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c8ad9a..d320cd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,24 @@ The format is based on [Keep a Changelog][1], and this project adheres to [Seman [1]: https://keepachangelog.com/en/1.0.0/ [2]: https://semver.org/spec/v2.0.0.html -## [Unreleased] +## majic [Unreleased] + +## Added + +- Forked gen_magic. +- Pool: `Majic.Pool` +- Unified API: `Majic.perform/1,2,3` + +## Changed + +- C port now using erl_interface +- `Majic.Server.reload/2,3` +- `Majic.Server.recycle/2,3` +- Bytes support: `Majic.Server.perform(ref, {:bytes, <<>>})` +- Builds on Musl +- Better error and timeout handling + +## gen_majic [1.0] ### Added diff --git a/Makefile b/Makefile index cbd6fec..ce342ea 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,3 @@ -# Apprentice binary - ERL_EI_INCLUDE:=$(shell erl -eval 'io:format("~s", [code:lib_dir(erl_interface, include)])' -s init stop -noshell | head -1) ERL_EI_LIB:=$(shell erl -eval 'io:format("~s", [code:lib_dir(erl_interface, lib)])' -s init stop -noshell | head -1) CFLAGS = -std=c99 -g -Wall -Werror @@ -9,9 +7,9 @@ LDLIBS = -lpthread -lei -lm -lmagic PRIV = priv/ RM = rm -Rf -all: priv/apprentice +all: priv/libmagic_port -priv/apprentice: src/apprentice.c +priv/libmagic_port: src/libmagic_port.c mkdir -p priv $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ diff --git a/README.md b/README.md index 7d1d402..99d6e82 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,24 @@ -# GenMagic +# Majic -**GenMagic** provides supervised and customisable access to [libmagic](http://man7.org/linux/man-pages/man3/libmagic.3.html) using a supervised external process. +**Majic** provides a robust integration of [libmagic](http://man7.org/linux/man-pages/man3/libmagic.3.html) for Elixir. -With this library, you can start an one-off process to run a single check, or run the process as a daemon if you expect to run many checks. +With this library, you can start an one-off process to run a single check, or run the process as a daemon if you expect to run +many checks. + +It is a friendly fork of [gen_magic](https://github.com/evadne/gen_magic) featuring a (arguably) more robust C-code +using erl_interface, built in pooling, unified/clean API, and an optional Plug. + +This package is regulary tested on multiple platforms (Debian, macOS, Fedora, Alpine, FreeBSD) to ensure it'll work fine +in any environment. ## Installation -The package can be installed by adding `gen_magic` to your list of dependencies in `mix.exs`: +The package can be installed by adding `majic` to your list of dependencies in `mix.exs`: ```elixir def deps do [ - {:gen_magic, "~> 1.0.0"} + {:majic, "~> 1.0"} ] end ``` @@ -22,34 +29,34 @@ Compilation of the underlying C program is automatic and handled by [elixir_make ## Usage -Depending on the use case, you may utilise a single (one-off) GenMagic process without reusing it as a daemon, or utilise a connection pool (such as Poolboy) in your application to run multiple persistent GenMagic processes. +Depending on the use case, you may utilise a single (one-off) Majic process without reusing it as a daemon, or utilise a connection pool (such as Poolboy) in your application to run multiple persistent Majic processes. -To use GenMagic directly, you can use `GenMagic.Helpers.perform_once/1`: +To use Majic directly, you can use `Majic.Helpers.perform_once/1`: ```elixir -iex(1)> GenMagic.perform(".", once: true) +iex(1)> Majic.perform(".", once: true) {:ok, - %GenMagic.Result{ + %Majic.Result{ content: "directory", encoding: "binary", mime_type: "inode/directory" }} ``` -To use the GenMagic server as a daemon, you can start it first, keep a reference, then feed messages to it as you require: +To use the Majic server as a daemon, you can start it first, keep a reference, then feed messages to it as you require: ```elixir -{:ok, pid} = GenMagic.Server.start_link([]) -{:ok, result} = GenMagic.perform(path, server: pid) +{:ok, pid} = Majic.Server.start_link([]) +{:ok, result} = Majic.perform(path, server: pid) ``` -See `GenMagic.Server.start_link/1` and `t:GenMagic.Server.option/0` for more information on startup parameters. +See `Majic.Server.start_link/1` and `t:Majic.Server.option/0` for more information on startup parameters. -See `GenMagic.Result` for details on the result provided. +See `Majic.Result` for details on the result provided. ## Configuration -When using `GenMagic.Server.start_link/1` to start a persistent server, or `GenMagic.Helpers.perform_once/2` to run an ad-hoc request, you can override specific options to suit your use case. +When using `Majic.Server.start_link/1` to start a persistent server, or `Majic.Helpers.perform_once/2` to run an ad-hoc request, you can override specific options to suit your use case. | Name | Default | Description | | - | - | - | @@ -58,18 +65,18 @@ When using `GenMagic.Server.start_link/1` to start a persistent server, or `GenM | `:recycle_threshold` | 10 | Number of cycles before the C process is replaced | | `:database_patterns` | `[:default]` | Databases to load | -See `t:GenMagic.Server.option/0` for details. +See `t:Majic.Server.option/0` for details. ### Use Cases ### Ad-Hoc Requests -For ad-hoc requests, you can use the helper method `GenMagic.Helpers.perform_once/2`: +For ad-hoc requests, you can use the helper method `Majic.Helpers.perform_once/2`: ```elixir -iex(1)> GenMagic.perform(Path.join(File.cwd!(), "Makefile"), once: true) +iex(1)> Majic.perform(Path.join(File.cwd!(), "Makefile"), once: true) {:ok, - %GenMagic.Result{ + %Majic.Result{ content: "makefile script, ASCII text", encoding: "us-ascii", mime_type: "text/x-makefile" @@ -83,22 +90,22 @@ The Server should be run under a supervisor which provides resiliency. Here we run it under a supervisor: ```elixir -iex(1)> {:ok, pid} = Supervisor.start_link([{GenMagic.Server, name: :gen_magic}], strategy: :one_for_one) +iex(1)> {:ok, pid} = Supervisor.start_link([{Majic.Server, name: :gen_magic}], strategy: :one_for_one) {:ok, #PID<0.199.0>} ``` Now we can ask it to inspect a file: ```elixir -iex(2)> GenMagic.perform(Path.expand("~/.bash_history"), server: :gen_magic) -{:ok, %GenMagic.Result{mime_type: "text/plain", encoding: "us-ascii", content: "ASCII text"}} +iex(2)> Majic.perform(Path.expand("~/.bash_history"), server: :gen_magic) +{:ok, %Majic.Result{mime_type: "text/plain", encoding: "us-ascii", content: "ASCII text"}} ``` Note that in this case we have opted to use a named process. ### Pool -For concurrency *and* resiliency, you may start the `GenMagic.Pool`. By default, it will start a `GenMagic.Server` +For concurrency *and* resiliency, you may start the `Majic.Pool`. By default, it will start a `Majic.Server` worker per online scheduler: You can add a pool in your application supervisor by adding it as a child: @@ -107,18 +114,18 @@ You can add a pool in your application supervisor by adding it as a child: children = [ # ... - {GenMagic.Pool, [name: YourApp.GenMagicPool, pool_size: 2]} + {Majic.Pool, [name: YourApp.MajicPool, pool_size: 2]} ] - opts = [strategy: :one_for_one, name: Pleroma.Supervisor] + opts = [strategy: :one_for_one, name: YourApp.Supervisor] Supervisor.start_link(children, opts) ``` -And then you can use it with `GenMagic.perform/2` with `pool: YourApp.GenMagicPool` option: +And then you can use it with `Majic.perform/2` with `pool: YourApp.MajicPool` option: ``` -iex(1)> GenMagic.perform(Path.expand("~/.bash_history"), pool: YourApp.GenMagicPool) -{:ok, %GenMagic.Result{mime_type: "text/plain", encoding: "us-ascii", content: "ASCII text"}} +iex(1)> Majic.perform(Path.expand("~/.bash_history"), pool: YourApp.MajicPool) +{:ok, %Majic.Result{mime_type: "text/plain", encoding: "us-ascii", content: "ASCII text"}} ``` ### Check Uploaded Files @@ -127,12 +134,12 @@ If you use Phoenix, you can inspect the file from your controller: ```elixir def upload(conn, %{"upload" => %{path: path}}) do, - {:ok, result} = GenMagic.perform(path, server: :gen_magic) + {:ok, result} = Majic.perform(path, server: :gen_magic) text(conn, "Received your file containing #{result.content}") end ``` -Obviously, it will be more ideal if you have wrapped `GenMagic.Server` in a pool such as Poolboy, to avoid constantly starting and stopping the underlying C program. +Obviously, it will be more ideal if you have wrapped `Majic.Server` in a pool such as Poolboy, to avoid constantly starting and stopping the underlying C program. ## Notes @@ -147,8 +154,11 @@ find . -name *ex | xargs mix run test/soak.exs ## Acknowledgements -During design and prototype development of this library, the Author has drawn inspiration from the following individuals, and therefore thanks all contributors for their generosity: +During design and prototype development of this library, the Author has drawn inspiration from the following individuals, and therefore +thanks all contributors for their generosity: +- [Evadne Wu](https://github.com/evadne) + - Original work - Mr [James Every](https://github.com/devstopfix) - Enhanced Elixir Wrapper (based on GenServer) - Initial Hex packaging (v.0.22) diff --git a/lib/gen_magic.ex b/lib/majic.ex similarity index 74% rename from lib/gen_magic.ex rename to lib/majic.ex index 292dfe8..0b1b2d0 100644 --- a/lib/gen_magic.ex +++ b/lib/majic.ex @@ -1,17 +1,11 @@ -defmodule GenMagic do - @moduledoc """ - Top-level namespace for GenMagic, the libmagic client for Elixir. - - See `GenMagic.Server` or the README for usage. - """ - +defmodule Majic do @doc """ Perform on `path`. An option of `server: ServerName`, `pool: PoolName` or `once: true` must be passed. """ + @type name :: {:pool, atom()} | {:server, GenMagic.Server.t()} | {:once, true} @type option :: name - when name: {:pool, atom()} | {:server, GenMagic.Server.t()} | {:once, true} @spec perform(GenMagic.Server.target(), [option()]) :: GenMagic.Server.result() def perform(path, opts, timeout \\ 5000) do @@ -34,7 +28,7 @@ defmodule GenMagic do end defp do_perform({mod, name}, path, timeout) do - mod.perform(name, path, tiemout) + mod.perform(name, path, timeout) end end diff --git a/lib/gen_magic/config.ex b/lib/majic/config.ex similarity index 94% rename from lib/gen_magic/config.ex rename to lib/majic/config.ex index 70ae355..a591c37 100644 --- a/lib/gen_magic/config.ex +++ b/lib/majic/config.ex @@ -1,7 +1,7 @@ -defmodule GenMagic.Config do +defmodule Majic.Config do @moduledoc false @otp_app Mix.Project.config()[:app] - @executable_name "apprentice" + @executable_name "libmagic_port" @startup_timeout 1_000 @process_timeout 30_000 @recycle_threshold :infinity diff --git a/lib/gen_magic/helpers.ex b/lib/majic/helpers.ex similarity index 70% rename from lib/gen_magic/helpers.ex rename to lib/majic/helpers.ex index 13ab3de..f4eb3cd 100644 --- a/lib/gen_magic/helpers.ex +++ b/lib/majic/helpers.ex @@ -1,10 +1,10 @@ -defmodule GenMagic.Helpers do +defmodule Majic.Helpers do @moduledoc """ Contains convenience functions for one-off use. """ - alias GenMagic.Result - alias GenMagic.Server + alias Majic.Result + alias Majic.Server @spec perform_once(Path.t() | {:bytes, binary}, [Server.option()]) :: {:ok, Result.t()} | {:error, term()} @@ -16,9 +16,9 @@ defmodule GenMagic.Helpers do ## Example - iex(1)> {:ok, result} = GenMagic.Helpers.perform_once(".") + iex(1)> {:ok, result} = Majic.Helpers.perform_once(".") iex(2)> result - %GenMagic.Result{content: "directory", encoding: "binary", mime_type: "inode/directory"} + %Majic.Result{content: "directory", encoding: "binary", mime_type: "inode/directory"} """ def perform_once(path, options \\ []) do with {:ok, pid} <- Server.start_link(options), diff --git a/lib/gen_magic/pool.ex b/lib/majic/pool.ex similarity index 87% rename from lib/gen_magic/pool.ex rename to lib/majic/pool.ex index f542b30..e28aca8 100644 --- a/lib/gen_magic/pool.ex +++ b/lib/majic/pool.ex @@ -1,6 +1,6 @@ -defmodule GenMagic.Pool do +defmodule Majic.Pool do @behaviour NimblePool - @moduledoc "Pool of `GenMagic.Server`" + @moduledoc "Pool of `Majic.Server`" def child_spec(opts) do %{ @@ -25,7 +25,7 @@ defmodule GenMagic.Pool do pool, :checkout, fn _, server -> - {GenMagic.Server.perform(server, path, timeout), server} + {Majic.Server.perform(server, path, timeout), server} end, pool_timeout ) @@ -46,7 +46,7 @@ defmodule GenMagic.Pool do @impl NimblePool def init_worker(options) do - {:ok, server} = GenMagic.Server.start_link(options || []) + {:ok, server} = Majic.Server.start_link(options || []) {:ok, server, options} end diff --git a/lib/gen_magic/result.ex b/lib/majic/result.ex similarity index 96% rename from lib/gen_magic/result.ex rename to lib/majic/result.ex index 0cfa361..c84b544 100644 --- a/lib/gen_magic/result.ex +++ b/lib/majic/result.ex @@ -1,4 +1,4 @@ -defmodule GenMagic.Result do +defmodule Majic.Result do @moduledoc """ Represents the results obtained from libmagic. diff --git a/lib/gen_magic/server.ex b/lib/majic/server.ex similarity index 98% rename from lib/gen_magic/server.ex rename to lib/majic/server.ex index adb3c8a..2bc9a44 100644 --- a/lib/gen_magic/server.ex +++ b/lib/majic/server.ex @@ -1,4 +1,4 @@ -defmodule GenMagic.Server do +defmodule Majic.Server do @moduledoc """ Provides access to the underlying libmagic client, which performs file introspection. @@ -6,9 +6,9 @@ defmodule GenMagic.Server do """ @behaviour :gen_statem - alias GenMagic.Result - alias GenMagic.Server.Data - alias GenMagic.Server.Status + alias Majic.Result + alias Majic.Server.Data + alias Majic.Server.Status import Kernel, except: [send: 2] require Logger @@ -34,7 +34,7 @@ defmodule GenMagic.Server do Can be set to `:infinity`. Please note that, if you have chosen a custom timeout value, you should also pass it when - using `GenMagic.Server.perform/3`. + using `Majic.Server.perform/3`. - `:recycle_threshold`: Specifies the number of requests processed before the underlying C program is recycled. @@ -169,7 +169,7 @@ defmodule GenMagic.Server do @impl :gen_statem def init(options) do - import GenMagic.Config + import Majic.Config data = %Data{ port_name: get_port_name(), diff --git a/lib/gen_magic/server/data.ex b/lib/majic/server/data.ex similarity index 95% rename from lib/gen_magic/server/data.ex rename to lib/majic/server/data.ex index 25d23e9..86c076b 100644 --- a/lib/gen_magic/server/data.ex +++ b/lib/majic/server/data.ex @@ -1,4 +1,4 @@ -defmodule GenMagic.Server.Data do +defmodule Majic.Server.Data do @moduledoc false @type request :: {Path.t(), {pid(), term()}, requested_at :: integer()} diff --git a/lib/gen_magic/server/status.ex b/lib/majic/server/status.ex similarity index 84% rename from lib/gen_magic/server/status.ex rename to lib/majic/server/status.ex index 7279b57..0df3a9d 100644 --- a/lib/gen_magic/server/status.ex +++ b/lib/majic/server/status.ex @@ -1,4 +1,4 @@ -defmodule GenMagic.Server.Status do +defmodule Majic.Server.Status do @moduledoc """ Represents Status of the underlying Server. """ @@ -12,7 +12,7 @@ defmodule GenMagic.Server.Status do recycling is enabled. """ @type t :: %__MODULE__{ - state: GenMagic.Server.state(), + state: Majic.Server.state(), cycles: non_neg_integer() } diff --git a/mix.exs b/mix.exs index 83c1597..4e9b699 100644 --- a/mix.exs +++ b/mix.exs @@ -1,13 +1,13 @@ -defmodule GenMagic.MixProject do +defmodule Majic.MixProject do use Mix.Project if :erlang.system_info(:otp_release) < '21' do - raise "GenMagic requires Erlang/OTP 21 or newer" + raise "Majic requires Erlang/OTP 21 or newer" end def project do [ - app: :gen_magic, + app: :majic, version: "1.0.0", elixir: "~> 1.7", elixirc_paths: elixirc_paths(Mix.env()), @@ -16,9 +16,9 @@ defmodule GenMagic.MixProject do package: package(), deps: deps(), dialyzer: dialyzer(), - name: "GenMagic", + name: "Majic", description: "File introspection with libmagic", - source_url: "https://github.com/evadne/gen_magic", + source_url: "https://github.com/hrefhref/majic", docs: docs() ] end diff --git a/src/apprentice.c b/src/libmagic_port.c similarity index 100% rename from src/apprentice.c rename to src/libmagic_port.c diff --git a/test/gen_magic/gen_magic_test.exs b/test/gen_magic/gen_magic_test.exs deleted file mode 100644 index 92574f4..0000000 --- a/test/gen_magic/gen_magic_test.exs +++ /dev/null @@ -1,69 +0,0 @@ -defmodule GenMagicTest do - use GenMagic.MagicCase - alias GenMagic.Result - - doctest GenMagic - @iterations 100 - - test "Makefile is text file" do - {:ok, pid} = GenMagic.Server.start_link([]) - path = absolute_path("Makefile") - assert {:ok, %{mime_type: "text/x-makefile"}} = GenMagic.Server.perform(pid, path) - end - - @tag external: true - test "Load test local files" do - {:ok, pid} = GenMagic.Server.start_link([]) - - files_stream() - |> Stream.cycle() - |> Stream.take(@iterations) - |> Stream.map(&assert {:ok, %Result{}} = GenMagic.Server.perform(pid, &1)) - |> Enum.all?() - |> assert - end - - test "Non-existent file" do - {:ok, pid} = GenMagic.Server.start_link([]) - path = missing_filename() - assert_no_file(GenMagic.Server.perform(pid, path)) - end - - test "Named process" do - {:ok, pid} = GenMagic.Server.start_link(name: :gen_magic) - path = absolute_path("Makefile") - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(:gen_magic) - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - assert {:ok, %Result{} = result} = GenMagic.Server.perform(:gen_magic, path) - assert {:ok, %{cycles: 1}} = GenMagic.Server.status(:gen_magic) - assert {:ok, %{cycles: 1}} = GenMagic.Server.status(pid) - assert "text/x-makefile" = result.mime_type - end - - describe "custom database" do - setup do - database = absolute_path("elixir.mgc") - on_exit(fn -> File.rm(database) end) - {_, 0} = System.cmd("file", ["-C", "-m", absolute_path("test/elixir")]) - [database: database] - end - - test "recognises Elixir files", %{database: database} do - {:ok, pid} = GenMagic.Server.start_link(database_patterns: [database]) - path = absolute_path("mix.exs") - assert {:ok, %Result{} = result} = GenMagic.Server.perform(pid, path) - assert "text/x-elixir" = result.mime_type - assert "us-ascii" = result.encoding - assert "Elixir module source text" = result.content - end - - test "recognises Elixir files after a reload", %{database: database} do - {:ok, pid} = GenMagic.Server.start_link([]) - path = absolute_path("mix.exs") - {:ok, %Result{mime_type: mime}} = GenMagic.Server.perform(pid, path) - refute mime == "text/x-elixir" - :ok = GenMagic.Server.reload(pid, [database]) - assert {:ok, %Result{mime_type: "text/x-elixir"}} = GenMagic.Server.perform(pid, path) - end - end -end diff --git a/test/gen_magic/helpers_test.exs b/test/gen_magic/helpers_test.exs deleted file mode 100644 index d2690ef..0000000 --- a/test/gen_magic/helpers_test.exs +++ /dev/null @@ -1,9 +0,0 @@ -defmodule GenMagic.HelpersTest do - use GenMagic.MagicCase - doctest GenMagic.Helpers - - test "perform_once" do - path = absolute_path("Makefile") - assert {:ok, %{mime_type: "text/x-makefile"}} = GenMagic.Helpers.perform_once(path) - end -end diff --git a/test/gen_magic/pool_test.exs b/test/gen_magic/pool_test.exs deleted file mode 100644 index b987186..0000000 --- a/test/gen_magic/pool_test.exs +++ /dev/null @@ -1,16 +0,0 @@ -defmodule GenMagic.PoollTest do - use GenMagic.MagicCase - - test "pool" do - {:ok, _} = GenMagic.Pool.start_link(name: TestPool, pool_size: 2) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - assert {:ok, _} = GenMagic.Pool.perform(TestPool, absolute_path("Makefile")) - end -end diff --git a/test/gen_magic/server_test.exs b/test/gen_magic/server_test.exs deleted file mode 100644 index d00933a..0000000 --- a/test/gen_magic/server_test.exs +++ /dev/null @@ -1,44 +0,0 @@ -defmodule GenMagic.ServerTest do - use GenMagic.MagicCase - doctest GenMagic.Server - - describe "recycle_threshold" do - test "resets" do - {:ok, pid} = GenMagic.Server.start_link(recycle_threshold: 3) - path = absolute_path("Makefile") - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - assert {:ok, _} = GenMagic.Server.perform(pid, path) - assert {:ok, %{cycles: 1}} = GenMagic.Server.status(pid) - assert {:ok, _} = GenMagic.Server.perform(pid, path) - assert {:ok, %{cycles: 2}} = GenMagic.Server.status(pid) - assert {:ok, _} = GenMagic.Server.perform(pid, path) - Process.sleep(100) - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - end - - test "resets before reply" do - {:ok, pid} = GenMagic.Server.start_link(recycle_threshold: 1) - path = absolute_path("Makefile") - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - assert {:ok, _} = GenMagic.Server.perform(pid, path) - Process.sleep(100) - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - assert {:ok, _} = GenMagic.Server.perform(pid, path) - Process.sleep(100) - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - assert {:ok, _} = GenMagic.Server.perform(pid, path) - Process.sleep(100) - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - end - end - - test "recycle" do - {:ok, pid} = GenMagic.Server.start_link([]) - path = absolute_path("Makefile") - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - assert {:ok, _} = GenMagic.Server.perform(pid, path) - assert {:ok, %{cycles: 1}} = GenMagic.Server.status(pid) - assert :ok = GenMagic.Server.recycle(pid) - assert {:ok, %{cycles: 0}} = GenMagic.Server.status(pid) - end -end diff --git a/test/majic/helpers_test.exs b/test/majic/helpers_test.exs new file mode 100644 index 0000000..ce514c5 --- /dev/null +++ b/test/majic/helpers_test.exs @@ -0,0 +1,9 @@ +defmodule Majic.HelpersTest do + use Majic.MagicCase + doctest Majic.Helpers + + test "perform_once" do + path = absolute_path("Makefile") + assert {:ok, %{mime_type: "text/x-makefile"}} = Majic.Helpers.perform_once(path) + end +end diff --git a/test/majic/majic_test.exs b/test/majic/majic_test.exs new file mode 100644 index 0000000..a47812d --- /dev/null +++ b/test/majic/majic_test.exs @@ -0,0 +1,69 @@ +defmodule MajicTest do + use Majic.MagicCase + alias Majic.Result + + doctest Majic + @iterations 100 + + test "Makefile is text file" do + {:ok, pid} = Majic.Server.start_link([]) + path = absolute_path("Makefile") + assert {:ok, %{mime_type: "text/x-makefile"}} = Majic.Server.perform(pid, path) + end + + @tag external: true + test "Load test local files" do + {:ok, pid} = Majic.Server.start_link([]) + + files_stream() + |> Stream.cycle() + |> Stream.take(@iterations) + |> Stream.map(&assert {:ok, %Result{}} = Majic.Server.perform(pid, &1)) + |> Enum.all?() + |> assert + end + + test "Non-existent file" do + {:ok, pid} = Majic.Server.start_link([]) + path = missing_filename() + assert_no_file(Majic.Server.perform(pid, path)) + end + + test "Named process" do + {:ok, pid} = Majic.Server.start_link(name: :gen_magic) + path = absolute_path("Makefile") + assert {:ok, %{cycles: 0}} = Majic.Server.status(:gen_magic) + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + assert {:ok, %Result{} = result} = Majic.Server.perform(:gen_magic, path) + assert {:ok, %{cycles: 1}} = Majic.Server.status(:gen_magic) + assert {:ok, %{cycles: 1}} = Majic.Server.status(pid) + assert "text/x-makefile" = result.mime_type + end + + describe "custom database" do + setup do + database = absolute_path("elixir.mgc") + on_exit(fn -> File.rm(database) end) + {_, 0} = System.cmd("file", ["-C", "-m", absolute_path("test/elixir")]) + [database: database] + end + + test "recognises Elixir files", %{database: database} do + {:ok, pid} = Majic.Server.start_link(database_patterns: [database]) + path = absolute_path("mix.exs") + assert {:ok, %Result{} = result} = Majic.Server.perform(pid, path) + assert "text/x-elixir" = result.mime_type + assert "us-ascii" = result.encoding + assert "Elixir module source text" = result.content + end + + test "recognises Elixir files after a reload", %{database: database} do + {:ok, pid} = Majic.Server.start_link([]) + path = absolute_path("mix.exs") + {:ok, %Result{mime_type: mime}} = Majic.Server.perform(pid, path) + refute mime == "text/x-elixir" + :ok = Majic.Server.reload(pid, [database]) + assert {:ok, %Result{mime_type: "text/x-elixir"}} = Majic.Server.perform(pid, path) + end + end +end diff --git a/test/majic/pool_test.exs b/test/majic/pool_test.exs new file mode 100644 index 0000000..12f8edf --- /dev/null +++ b/test/majic/pool_test.exs @@ -0,0 +1,16 @@ +defmodule Majic.PoollTest do + use Majic.MagicCase + + test "pool" do + {:ok, _} = Majic.Pool.start_link(name: TestPool, pool_size: 2) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + assert {:ok, _} = Majic.Pool.perform(TestPool, absolute_path("Makefile")) + end +end diff --git a/test/gen_magic/apprentice_test.exs b/test/majic/port_test.exs similarity index 93% rename from test/gen_magic/apprentice_test.exs rename to test/majic/port_test.exs index dfb86a0..7c5f089 100644 --- a/test/gen_magic/apprentice_test.exs +++ b/test/majic/port_test.exs @@ -1,17 +1,17 @@ -defmodule GenMagic.ApprenticeTest do - use GenMagic.MagicCase +defmodule Majic.ApprenticeTest do + use Majic.MagicCase @tmp_path "/tmp/testgenmagicx" require Logger test "sends ready" do - port = Port.open(GenMagic.Config.get_port_name(), GenMagic.Config.get_port_options([])) + port = Port.open(Majic.Config.get_port_name(), Majic.Config.get_port_options([])) on_exit(fn -> send(port, {self(), :close}) end) assert_ready_and_init_default(port) end test "stops" do - port = Port.open(GenMagic.Config.get_port_name(), GenMagic.Config.get_port_options([])) + port = Port.open(Majic.Config.get_port_name(), Majic.Config.get_port_options([])) on_exit(fn -> send(port, {self(), :close}) end) assert_ready_and_init_default(port) send(port, {self(), {:command, :erlang.term_to_binary({:stop, :stop})}}) @@ -20,7 +20,7 @@ defmodule GenMagic.ApprenticeTest do test "exits with non existent database with an error" do opts = [:use_stdio, :binary, :exit_status, {:packet, 2}, {:args, []}] - port = Port.open(GenMagic.Config.get_port_name(), opts) + port = Port.open(Majic.Config.get_port_name(), opts) on_exit(fn -> send(port, {self(), :close}) end) assert_ready(port) @@ -34,7 +34,7 @@ defmodule GenMagic.ApprenticeTest do describe "port" do setup do - port = Port.open(GenMagic.Config.get_port_name(), GenMagic.Config.get_port_options([])) + port = Port.open(Majic.Config.get_port_name(), Majic.Config.get_port_options([])) on_exit(fn -> send(port, {self(), :close}) end) assert_ready_and_init_default(port) %{port: port} diff --git a/test/majic/server_test.exs b/test/majic/server_test.exs new file mode 100644 index 0000000..d9b2c7a --- /dev/null +++ b/test/majic/server_test.exs @@ -0,0 +1,44 @@ +defmodule Majic.ServerTest do + use Majic.MagicCase + doctest Majic.Server + + describe "recycle_threshold" do + test "resets" do + {:ok, pid} = Majic.Server.start_link(recycle_threshold: 3) + path = absolute_path("Makefile") + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + assert {:ok, _} = Majic.Server.perform(pid, path) + assert {:ok, %{cycles: 1}} = Majic.Server.status(pid) + assert {:ok, _} = Majic.Server.perform(pid, path) + assert {:ok, %{cycles: 2}} = Majic.Server.status(pid) + assert {:ok, _} = Majic.Server.perform(pid, path) + Process.sleep(100) + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + end + + test "resets before reply" do + {:ok, pid} = Majic.Server.start_link(recycle_threshold: 1) + path = absolute_path("Makefile") + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + assert {:ok, _} = Majic.Server.perform(pid, path) + Process.sleep(100) + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + assert {:ok, _} = Majic.Server.perform(pid, path) + Process.sleep(100) + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + assert {:ok, _} = Majic.Server.perform(pid, path) + Process.sleep(100) + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + end + end + + test "recycle" do + {:ok, pid} = Majic.Server.start_link([]) + path = absolute_path("Makefile") + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + assert {:ok, _} = Majic.Server.perform(pid, path) + assert {:ok, %{cycles: 1}} = Majic.Server.status(pid) + assert :ok = Majic.Server.recycle(pid) + assert {:ok, %{cycles: 0}} = Majic.Server.status(pid) + end +end diff --git a/test/soak.exs b/test/soak.exs index 87a40a2..8f9826c 100644 --- a/test/soak.exs +++ b/test/soak.exs @@ -8,7 +8,7 @@ defmodule Soak do def perform_infinite([]), do: false def perform_infinite(paths) do - {:ok, pid} = GenMagic.Server.start_link(database_patterns: ["/usr/local/share/misc/*.mgc"]) + {:ok, pid} = Majic.Server.start_link(database_patterns: ["/usr/local/share/misc/*.mgc"]) perform_infinite(paths, [], pid, 0) end @@ -19,7 +19,7 @@ defmodule Soak do defp perform_infinite([path | paths], done, pid, count) do if rem(count, 1000) == 0, do: IO.puts(Integer.to_string(count)) - {:ok, %GenMagic.Result{}} = GenMagic.Server.perform(pid, path) + {:ok, %Majic.Result{}} = Majic.Server.perform(pid, path) perform_infinite(paths, [path | done], pid, count + 1) end end diff --git a/test/support/magic_case.ex b/test/support/magic_case.ex index 53df01e..3ba7e69 100644 --- a/test/support/magic_case.ex +++ b/test/support/magic_case.ex @@ -1,4 +1,4 @@ -defmodule GenMagic.MagicCase do +defmodule Majic.MagicCase do @moduledoc false use ExUnit.CaseTemplate