diff --git a/lib/matchers/text.ex b/lib/matchers/text.ex index bafb8c3..96e5a71 100644 --- a/lib/matchers/text.ex +++ b/lib/matchers/text.ex @@ -19,6 +19,10 @@ defmodule Infer.Text do iex> Infer.Text.html?("<") false + iex> binary = File.read!("test/archives/sample.zip") + iex> Infer.Text.html?(binary) + false + """ @spec html?(binary()) :: boolean() def html?(binary) do @@ -45,7 +49,7 @@ defmodule Infer.Text do char_list = binary |> String.trim() - |> String.to_charlist() + |> :binary.bin_to_list() Enum.any?(values, fn val -> if starts_with_ignore_ascii_case(char_list, val) do @@ -62,19 +66,39 @@ defmodule Infer.Text do Takes the binary file contents as arguments. Returns `true` if it's xml. See: https://mimesniff.spec.whatwg.org/ + + ## Examples + + iex> Infer.Text.xml?(~s()) + true + + iex> binary = File.read!("test/archives/sample.zip") + iex> Infer.Text.xml?(binary) + false + """ @spec xml?(binary()) :: boolean() def xml?(binary) do char_list = binary |> String.trim() - |> String.to_charlist() + |> :binary.bin_to_list() starts_with_ignore_ascii_case(char_list, ' Infer.Text.shell_script?("#!/bin/sh") + true + + iex> binary = File.read!("test/archives/sample.zip") + iex> Infer.Text.shell_script?(binary) + false + """ @spec shell_script?(binary()) :: boolean() def shell_script?(<<"#!", _rest::binary>>), do: true diff --git a/mix.exs b/mix.exs index 02627c7..f82e9f2 100644 --- a/mix.exs +++ b/mix.exs @@ -6,6 +6,7 @@ defmodule Infer.MixProject do app: :infer, version: "0.2.0", elixir: "~> 1.10", + elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, deps: deps(), package: package(), @@ -29,6 +30,9 @@ defmodule Infer.MixProject do ] end + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + # Run "mix help deps" to learn about dependencies. defp deps do [ diff --git a/test/infer_test.exs b/test/infer_test.exs index 54b2cdf..607c73c 100644 --- a/test/infer_test.exs +++ b/test/infer_test.exs @@ -1,5 +1,6 @@ defmodule InferTest do use ExUnit.Case + doctest Infer doctest Infer.App doctest Infer.Archive @@ -10,4 +11,166 @@ defmodule InferTest do doctest Infer.Font doctest Infer.Text doctest Infer.Video + + describe "Infer.App" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :app)) + + test "handles app files" do + for {_path, binary} <- TestFiles.list(only: :app) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-app files" do + for {path, binary} <- TestFiles.list(except: :app) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end + + describe "Infer.Book" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :book)) + + test "handles book files" do + for {_path, binary} <- TestFiles.list(only: :books) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-book files" do + for {path, binary} <- TestFiles.list(except: :books) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end + + describe "Infer.Image" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :image)) + + test "handles image files" do + for {_path, binary} <- TestFiles.list(only: :images) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-image files" do + for {path, binary} <- TestFiles.list(except: :images) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end + + describe "Infer.Video" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :video)) + + test "handles video files" do + for {_path, binary} <- TestFiles.list(only: :videos) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-video files" do + for {path, binary} <- TestFiles.list(except: :videos) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end + + describe "Infer.Audio" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :audio)) + + test "handles audio files" do + for {_path, binary} <- TestFiles.list(only: :audio) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-audio files" do + for {path, binary} <- TestFiles.list(except: :audio) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end + + describe "Infer.Font" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :font)) + + test "handles font files" do + for {_path, binary} <- TestFiles.list(only: :fonts) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-font files" do + for {path, binary} <- TestFiles.list(except: :fonts) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end + + describe "Infer.Doc" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :doc)) + + test "handles documnet files" do + for {_path, binary} <- TestFiles.list(only: :docs) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-document files" do + for {path, binary} <- TestFiles.list(except: :docs) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end + + describe "Infer.Archive" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :archive)) + + test "handles archive files" do + for {_path, binary} <- TestFiles.list(only: :archives) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-archive files" do + for {path, binary} <- TestFiles.list(except: [:archives, :docs, :books]) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end + + describe "Infer.Text" do + @matchers Infer.Matchers.list() |> Enum.filter(&(&1.matcher_type == :text)) + + test "handles text files" do + for {_path, binary} <- TestFiles.list(only: :text) do + assert Enum.find(@matchers, & &1.matcher.(binary)) + end + end + + for %Infer.Type{matcher: matcher} <- @matchers do + test "#{inspect(matcher)} handles non-text files" do + for {path, binary} <- TestFiles.list(except: :text) do + assert {_, false} = {path, unquote(matcher).(binary)} + end + end + end + end end diff --git a/test/support/test_files.ex b/test/support/test_files.ex new file mode 100644 index 0000000..c0db3d6 --- /dev/null +++ b/test/support/test_files.ex @@ -0,0 +1,20 @@ +defmodule TestFiles do + @types [:app, :archives, :audio, :books, :docs, :fonts, :images, :videos, :text] + @paths Map.new(@types, &{&1, Path.wildcard("test/#{&1}/*")}) + + def list(opts \\ []) do + requested_types = + case List.wrap(opts[:only]) do + [] -> @types -- List.wrap(opts[:except]) + only -> only + end + + Stream.flat_map(@paths, fn {type, paths} -> + if type in requested_types do + Enum.map(paths, &{Path.basename(&1), File.read!(&1)}) + else + [] + end + end) + end +end diff --git a/test/text/sample.html b/test/text/sample.html new file mode 100644 index 0000000..ba0d19f --- /dev/null +++ b/test/text/sample.html @@ -0,0 +1,7 @@ + + + +

My First Heading

+

My first paragraph.

+ + diff --git a/test/text/sample.sh b/test/text/sample.sh new file mode 100644 index 0000000..8dc4f64 --- /dev/null +++ b/test/text/sample.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Hello World!" diff --git a/test/text/sample.xml b/test/text/sample.xml new file mode 100644 index 0000000..df9948a --- /dev/null +++ b/test/text/sample.xml @@ -0,0 +1,120 @@ + + + + Gambardella, Matthew + XML Developer's Guide + Computer + 44.95 + 2000-10-01 + An in-depth look at creating applications + with XML. + + + Ralls, Kim + Midnight Rain + Fantasy + 5.95 + 2000-12-16 + A former architect battles corporate zombies, + an evil sorceress, and her own childhood to become queen + of the world. + + + Corets, Eva + Maeve Ascendant + Fantasy + 5.95 + 2000-11-17 + After the collapse of a nanotechnology + society in England, the young survivors lay the + foundation for a new society. + + + Corets, Eva + Oberon's Legacy + Fantasy + 5.95 + 2001-03-10 + In post-apocalypse England, the mysterious + agent known only as Oberon helps to create a new life + for the inhabitants of London. Sequel to Maeve + Ascendant. + + + Corets, Eva + The Sundered Grail + Fantasy + 5.95 + 2001-09-10 + The two daughters of Maeve, half-sisters, + battle one another for control of England. Sequel to + Oberon's Legacy. + + + Randall, Cynthia + Lover Birds + Romance + 4.95 + 2000-09-02 + When Carla meets Paul at an ornithology + conference, tempers fly as feathers get ruffled. + + + Thurman, Paula + Splish Splash + Romance + 4.95 + 2000-11-02 + A deep sea diver finds true love twenty + thousand leagues beneath the sea. + + + Knorr, Stefan + Creepy Crawlies + Horror + 4.95 + 2000-12-06 + An anthology of horror stories about roaches, + centipedes, scorpions and other insects. + + + Kress, Peter + Paradox Lost + Science Fiction + 6.95 + 2000-11-02 + After an inadvertant trip through a Heisenberg + Uncertainty Device, James Salway discovers the problems + of being quantum. + + + O'Brien, Tim + Microsoft .NET: The Programming Bible + Computer + 36.95 + 2000-12-09 + Microsoft's .NET initiative is explored in + detail in this deep programmer's reference. + + + O'Brien, Tim + MSXML3: A Comprehensive Guide + Computer + 36.95 + 2000-12-01 + The Microsoft MSXML3 parser is covered in + detail, with attention to XML DOM interfaces, XSLT processing, + SAX and more. + + + Galos, Mike + Visual Studio 7: A Comprehensive Guide + Computer + 49.95 + 2001-04-16 + Microsoft Visual Studio 7 is explored in depth, + looking at how Visual Basic, Visual C++, C#, and ASP+ are + integrated into a comprehensive development + environment. + +