defmodule ExVCR.Task.Runner do
@moduledoc """
Provides task processing logics, which will be invoked by custom mix tasks.
"""
@print_header_format " ~-40s ~-30s\n"
@check_header_format " ~-40s ~-20s ~-20s\n"
@check_content_format " ~-40s ~-20w ~-20w\n"
@date_format "~4..0B/~2..0B/~2..0B ~2..0B:~2..0B:~2..0B"
@json_file_pattern ~r/\.json$/
@doc """
Use specified path to show the list of vcr cassettes.
"""
def show_vcr_cassettes(path_list) do
Enum.each(path_list, fn(path) ->
if File.exists?(path) do
read_cassettes(path) |> print_cassettes(path)
IO.puts ""
end
end)
end
defp read_cassettes(path) do
file_names = find_json_files(path)
date_times = Enum.map(file_names, &(extract_last_modified_time(path, &1)))
Enum.zip(file_names, date_times)
end
defp find_json_files(path) do
if File.exists?(path) do
File.ls!(path)
|> Enum.filter(&(&1 =~ @json_file_pattern))
|> Enum.sort
else
raise ExVCR.PathNotFoundError, message: "Specified path '#{path}' for reading cassettes was not found."
end
end
defp extract_last_modified_time(path, file_name) do
{{year, month, day}, {hour, min, sec}} = File.stat!(Path.join(path, file_name)).mtime
sprintf(@date_format, [year, month, day, hour, min, sec])
end
defp print_cassettes(items, path) do
IO.puts "Showing list of cassettes in [#{path}]"
printf(@print_header_format, ["[File Name]", "[Last Update]"])
Enum.each(items, fn({name, date}) -> printf(@print_header_format, [name, date]) end)
end
@doc """
Use specified path to delete cassettes.
"""
def delete_cassettes(path, file_patterns, is_interactive \\ false) do
path |> find_json_files
|> Enum.filter(&(&1 =~ file_patterns))
|> Enum.each(&(delete_and_print_name(path, &1, is_interactive)))
end
defp delete_and_print_name(path, file_name, true) do
line = IO.gets("delete #{file_name}? ")
if String.upcase(line) == "Y\n" do
delete_and_print_name(path, file_name, false)
end
end
defp delete_and_print_name(path, file_name, false) do
case Path.expand(file_name, path) |> File.rm do
:ok -> IO.puts "Deleted #{file_name}."
:error -> IO.puts "Failed to delete #{file_name}"
end
end
@doc """
Check and show which cassettes are used by the test execution.
"""
def check_cassettes(record) do
count_hash = create_count_hash(record.files, %{})
Enum.each(record.dirs, fn(dir) ->
IO.puts "Showing hit counts of cassettes in [#{dir}]"
if File.exists?(dir) do
cassettes = read_cassettes(dir)
print_check_cassettes(cassettes, count_hash)
end
IO.puts ""
end)
end
defp create_count_hash([], acc), do: acc
defp create_count_hash([{type, path}|tail], acc) do
file = Path.basename(path)
counts = Map.get(acc, file, %ExVCR.Checker.Counts{})
hash = case type do
:cache -> Map.put(acc, file, %{counts | cache: counts.cache + 1})
:server -> Map.put(acc, file, %{counts | server: counts.server + 1})
end
create_count_hash(tail, hash)
end
defp print_check_cassettes(items, counts_hash) do
printf(@check_header_format, ["[File Name]", "[Cassette Counts]", "[Server Counts]"])
Enum.each(items, fn({name, _date}) ->
counts = Map.get(counts_hash, name, %ExVCR.Checker.Counts{})
printf(@check_content_format, [name, counts.cache, counts.server])
end)
end
defp printf(format, params) do
IO.write sprintf(format, params)
end
defp sprintf(format, params) do
char_list = :io_lib.format(format, params)
List.to_string(char_list)
end
end