This commit is contained in:
Jordan Bracco 2020-06-15 22:23:14 +02:00
parent 52f6aa7281
commit b5777bf9e8
31 changed files with 373 additions and 218 deletions

20
.builds/alpine.yaml Normal file
View file

@ -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

17
.builds/archlinux.yaml Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

17
.builds/freebsd.yaml Normal file
View file

@ -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

View file

@ -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

View file

@ -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 $@

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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),

View file

@ -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

View file

@ -1,4 +1,4 @@
defmodule GenMagic.Result do
defmodule Majic.Result do
@moduledoc """
Represents the results obtained from libmagic.

View file

@ -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(),

View file

@ -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()}

View file

@ -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()
}

10
mix.exs
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

69
test/majic/majic_test.exs Normal file
View file

@ -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

16
test/majic/pool_test.exs Normal file
View file

@ -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

View file

@ -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}

View file

@ -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

View file

@ -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

View file

@ -1,4 +1,4 @@
defmodule GenMagic.MagicCase do
defmodule Majic.MagicCase do
@moduledoc false
use ExUnit.CaseTemplate