187 lines
6 KiB
Elixir
187 lines
6 KiB
Elixir
defmodule SnmpKit.SnmpLib.MIB.ComprehensiveMibTest do
|
|
@moduledoc """
|
|
Comprehensive test suite for all MIB fixtures to ensure 100% compatibility
|
|
across working, broken, and DOCSIS MIB categories.
|
|
"""
|
|
|
|
use ExUnit.Case, async: true
|
|
|
|
alias SnmpKit.SnmpLib.MIB.Parser
|
|
|
|
@moduletag :mib
|
|
|
|
@test_dirs [
|
|
{"working", "test/fixtures/mibs/working"},
|
|
{"broken", "test/fixtures/mibs/broken"},
|
|
{"docsis", "test/fixtures/mibs/docsis"}
|
|
]
|
|
|
|
describe "MIB compatibility tests" do
|
|
for {dir_name, dir_path} <- @test_dirs do
|
|
test "#{dir_name} MIBs parse successfully" do
|
|
if Code.ensure_loaded?(:yecc) and function_exported?(:yecc, :file, 1) do
|
|
dir_path = Path.join(File.cwd!(), unquote(dir_path))
|
|
|
|
case File.ls(dir_path) do
|
|
{:ok, files} ->
|
|
mib_files = filter_mib_files(files)
|
|
|
|
assert mib_files != [], "No MIB files found in #{unquote(dir_name)} directory"
|
|
|
|
results = test_mib_files(dir_path, mib_files)
|
|
|
|
successful = Enum.count(results, fn {status, _} -> status == :ok end)
|
|
failed = Enum.filter(results, fn {status, _} -> status == :error end)
|
|
|
|
# Log results for visibility
|
|
IO.puts("\n#{String.upcase(unquote(dir_name))} MIBs: #{successful}/#{Enum.count(mib_files)} successful")
|
|
|
|
if failed != [] do
|
|
IO.puts("Failed files:")
|
|
|
|
for {:error, {file, reason}} <- failed do
|
|
reason_str =
|
|
case reason do
|
|
{line, module, message} when is_integer(line) and is_atom(module) ->
|
|
"Line #{line}: #{message}"
|
|
|
|
reason when is_binary(reason) ->
|
|
reason
|
|
|
|
_ ->
|
|
inspect(reason)
|
|
end
|
|
|
|
IO.puts(" - #{file}: #{reason_str}")
|
|
end
|
|
end
|
|
|
|
# All files should tokenize successfully
|
|
assert successful == length(mib_files),
|
|
"#{length(failed)} files failed to tokenize in #{unquote(dir_name)} directory"
|
|
|
|
{:error, _reason} ->
|
|
# Fixture directory doesn't exist — skip rather than fail
|
|
:ok
|
|
end
|
|
else
|
|
# yecc module not available - MIB parsing disabled, test passes
|
|
refute false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "performance benchmarks" do
|
|
test "lexer performance meets minimum thresholds" do
|
|
# Test with a sample from each directory type (use files that actually parse)
|
|
test_cases = [
|
|
# 500 definitions/sec minimum
|
|
{"working", "test/fixtures/mibs/working/IF-MIB.mib", 500},
|
|
{"working", "test/fixtures/mibs/working/HOST-RESOURCES-MIB.mib", 500},
|
|
{"docsis", "test/fixtures/mibs/docsis/DOCS-CABLE-DEVICE-MIB", 500}
|
|
]
|
|
|
|
for {type, file_path, min_rate} <- test_cases do
|
|
full_path = Path.join(File.cwd!(), file_path)
|
|
|
|
if File.exists?(full_path) do
|
|
content = File.read!(full_path)
|
|
|
|
# Warm up and verify file parses successfully
|
|
case Parser.parse(content) do
|
|
{:ok, _} ->
|
|
# Performance test
|
|
{time_us, {:ok, mib}} =
|
|
:timer.tc(fn ->
|
|
Parser.parse(content)
|
|
end)
|
|
|
|
# Calculate a rate based on definitions instead of tokens
|
|
definitions_count =
|
|
case mib do
|
|
%{definitions: defs} when is_list(defs) -> length(defs)
|
|
_ -> 1
|
|
end
|
|
|
|
rate = definitions_count / time_us * 1_000_000
|
|
|
|
assert rate >= min_rate,
|
|
"#{type} MIB performance too slow: #{Float.round(rate)} definitions/sec < #{min_rate}"
|
|
|
|
{:error, _reason} ->
|
|
# Skip performance test for files that don't parse
|
|
IO.puts("Skipping performance test for #{file_path} - parsing failed")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "memory efficiency tests" do
|
|
@tag :memory
|
|
test "tokenization does not leak memory" do
|
|
# Test with a medium-sized file repeatedly
|
|
file_path = Path.join(File.cwd!(), "test/fixtures/mibs/working/IF-MIB.mib")
|
|
|
|
if File.exists?(file_path) do
|
|
content = File.read!(file_path)
|
|
|
|
# Get baseline memory
|
|
:erlang.garbage_collect()
|
|
initial_memory = :erlang.memory(:total)
|
|
|
|
# Run parsing multiple times
|
|
for _ <- 1..100 do
|
|
{:ok, _mib} = Parser.parse(content)
|
|
end
|
|
|
|
# Force garbage collection and check memory
|
|
:erlang.garbage_collect()
|
|
final_memory = :erlang.memory(:total)
|
|
|
|
memory_increase = final_memory - initial_memory
|
|
memory_increase_mb = memory_increase / 1_024 / 1_024
|
|
|
|
# Should not leak significant memory (allow 10MB tolerance)
|
|
assert memory_increase_mb < 10,
|
|
"Memory leak detected: #{Float.round(memory_increase_mb, 2)}MB increase"
|
|
end
|
|
end
|
|
end
|
|
|
|
# Helper functions
|
|
|
|
defp filter_mib_files(files) do
|
|
files
|
|
|> Enum.filter(fn file ->
|
|
String.ends_with?(file, [".mib", ".MIB"]) or
|
|
(not String.contains?(file, ".") and not String.starts_with?(file, "download"))
|
|
end)
|
|
|> Enum.sort()
|
|
end
|
|
|
|
defp test_mib_files(dir_path, mib_files) do
|
|
Enum.map(mib_files, fn file ->
|
|
file_path = Path.join(dir_path, file)
|
|
test_single_mib_file(file_path, file)
|
|
end)
|
|
end
|
|
|
|
defp test_single_mib_file(file_path, file_name) do
|
|
with {:ok, content} <- File.read(file_path),
|
|
{:ok, mib} when is_map(mib) <- Parser.parse(content) do
|
|
definitions_count = get_definitions_count(mib)
|
|
{:ok, {file_name, definitions_count}}
|
|
else
|
|
{:error, reason} when is_binary(reason) ->
|
|
{:error, {file_name, "File read error: #{reason}"}}
|
|
|
|
{:error, reason} ->
|
|
{:error, {file_name, reason}}
|
|
end
|
|
end
|
|
|
|
defp get_definitions_count(%{definitions: defs}) when is_list(defs), do: length(defs)
|
|
defp get_definitions_count(_), do: 1
|
|
end
|