quality: Add test cases for instructions

This commit is contained in:
Eishun Kondoh 2019-04-28 23:57:22 +09:00
parent b4c6f0822c
commit a0ee397921
9 changed files with 120 additions and 43 deletions

View file

@ -1,18 +1,31 @@
defmodule Openflow.Instruction do defmodule Openflow.Instruction do
def read(instruction_bin) do @moduledoc """
do_read([], instruction_bin) Openflow instruction codec handler
end """
def to_binary(instructions) when is_list(instructions) do @type instruction ::
to_binary(<<>>, instructions) Openflow.Instruction.ApplyActions.t()
end | Openflow.Instruction.WriteActions.t()
| Openflow.Instruction.ClearActions.t()
| Openflow.Instruction.GotoTable.t()
| Openflow.Instruction.WriteMetadata.t()
| Openflow.Instruction.Meter.t()
def to_binary(instruction) do @spec read(<<_::32, _::_*8>>) :: [instruction()]
to_binary([instruction]) def read(instruction_bin),
end do: do_read([], instruction_bin)
@spec to_binary(instruction()) :: <<_::32, _::_*8>>
def to_binary(instructions) when is_list(instructions),
do: to_binary(<<>>, instructions)
@spec to_binary([instruction()]) :: <<_::32, _::_*8>>
def to_binary(instruction),
do: to_binary([instruction])
# private functions # private functions
@spec do_read([instruction()], <<_::_*8>>) :: [instruction()]
defp do_read(acc, <<>>), do: Enum.reverse(acc) defp do_read(acc, <<>>), do: Enum.reverse(acc)
defp do_read(acc, <<type::16, length::16, _::bytes>> = binary) do defp do_read(acc, <<type::16, length::16, _::bytes>> = binary) do
@ -21,6 +34,7 @@ defmodule Openflow.Instruction do
do_read([codec.read(instruction_bin) | acc], rest) do_read([codec.read(instruction_bin) | acc], rest)
end end
@spec to_binary(<<_::_*8>>) :: [instruction()]
defp to_binary(acc, []), do: acc defp to_binary(acc, []), do: acc
defp to_binary(acc, [instruction | rest]) do defp to_binary(acc, [instruction | rest]) do

View file

@ -3,16 +3,19 @@ defmodule Openflow.Instruction.ApplyActions do
alias __MODULE__ alias __MODULE__
def new(actions) do @type t :: %ApplyActions{actions: [Openflow.Action.type()]}
%ApplyActions{actions: actions}
end
@spec new(Openflow.Action.type() | [Openflow.Action.type()]) :: t()
def new(actions), do: %ApplyActions{actions: actions}
@spec to_binary(t()) :: <<_::64, _::_*8>>
def to_binary(%ApplyActions{actions: actions}) do def to_binary(%ApplyActions{actions: actions}) do
actions_bin = Openflow.Action.to_binary(actions) actions_bin = Openflow.Action.to_binary(actions)
length = 8 + byte_size(actions_bin) length = 8 + byte_size(actions_bin)
<<4::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>> <<4::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>>
end end
@spec read(<<_::64, _::_*8>>) :: t()
def read(<<4::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do def read(<<4::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do
actions = Openflow.Action.read(actions_bin) actions = Openflow.Action.read(actions_bin)
%ApplyActions{actions: actions} %ApplyActions{actions: actions}

View file

@ -3,17 +3,17 @@ defmodule Openflow.Instruction.ClearActions do
alias __MODULE__ alias __MODULE__
def new do @type t :: %ClearActions{}
%ClearActions{}
end
def to_binary(%ClearActions{}) do @spec new() :: t()
actions_bin = "" def new,
length = 8 + byte_size(actions_bin) do: %ClearActions{}
<<5::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>>
end
def read(<<5::16, _length::16, _::size(4)-unit(8), _::bytes>>) do @spec to_binary(t()) :: <<_::64>>
%ClearActions{} def to_binary(%ClearActions{}),
end do: <<5::16, 8::16, 0::32>>
@spec read(<<_::64, _::_*8>>) :: t()
def read(<<5::16, _length::16, _::size(4)-unit(8), _::bytes>>),
do: %ClearActions{}
end end

View file

@ -3,15 +3,19 @@ defmodule Openflow.Instruction.Experimenter do
alias __MODULE__ alias __MODULE__
def new(exp_id, data \\ "") do @type t :: %Experimenter{exp_id: 0..0xFFFFFFFF, data: binary()}
%Experimenter{exp_id: exp_id, data: data}
end
@spec new(exp_id :: 0..0xFFFFFFFF, data :: String.t()) :: t()
def new(exp_id, data \\ ""),
do: %Experimenter{exp_id: exp_id, data: data}
@spec to_binary(t()) :: <<_::64, _::_*8>>
def to_binary(%Experimenter{exp_id: exp_id, data: data}) do def to_binary(%Experimenter{exp_id: exp_id, data: data}) do
length = 8 + byte_size(data) length = 8 + byte_size(data)
<<0xFFFF::16, length::16, exp_id::32, data::bytes>> <<0xFFFF::16, length::16, exp_id::32, data::bytes>>
end end
@spec read(<<_::64, _::_*8>>) :: t()
def read(<<0xFFFF::16, _::16, exp_id::32, data::bytes>>) do def read(<<0xFFFF::16, _::16, exp_id::32, data::bytes>>) do
%Experimenter{exp_id: exp_id, data: data} %Experimenter{exp_id: exp_id, data: data}
end end

View file

@ -3,15 +3,18 @@ defmodule Openflow.Instruction.GotoTable do
alias __MODULE__ alias __MODULE__
def new(table_id) do @type t :: %GotoTable{table_id: 0..0xFF}
%GotoTable{table_id: table_id}
end
@spec new(table_id :: 0..0xFF) :: t()
def new(table_id), do: %GotoTable{table_id: table_id}
@spec to_binary(t()) :: <<_::64>>
def to_binary(%GotoTable{table_id: table_id}) do def to_binary(%GotoTable{table_id: table_id}) do
table_id_int = Openflow.Utils.get_enum(table_id, :table_id) table_id_int = Openflow.Utils.get_enum(table_id, :table_id)
<<1::16, 8::16, table_id_int::8, 0::size(3)-unit(8)>> <<1::16, 8::16, table_id_int::8, 0::size(3)-unit(8)>>
end end
@spec read(<<_::64>>) :: t()
def read(<<1::16, 8::16, table_id_int::8, _::size(3)-unit(8)>>) do def read(<<1::16, 8::16, table_id_int::8, _::size(3)-unit(8)>>) do
table_id = Openflow.Utils.get_enum(table_id_int, :table_id) table_id = Openflow.Utils.get_enum(table_id_int, :table_id)
%GotoTable{table_id: table_id} %GotoTable{table_id: table_id}

View file

@ -3,15 +3,18 @@ defmodule Openflow.Instruction.Meter do
alias __MODULE__ alias __MODULE__
def new(meter_id) do @type t :: %Meter{meter_id: 0..0xFFFFFFFF}
%Meter{meter_id: meter_id}
end
@spec new(meter_id :: 0..0xFFFFFFFF) :: t()
def new(meter_id), do: %Meter{meter_id: meter_id}
@spec to_binary(t()) :: <<_::64>>
def to_binary(%Meter{meter_id: meter_id}) do def to_binary(%Meter{meter_id: meter_id}) do
meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id)
<<6::16, 8::16, meter_id_int::32>> <<6::16, 8::16, meter_id_int::32>>
end end
@spec read(<<_::64>>) :: t()
def read(<<6::16, _::16, meter_id_int::32>>) do def read(<<6::16, _::16, meter_id_int::32>>) do
meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id)
%Meter{meter_id: meter_id} %Meter{meter_id: meter_id}

View file

@ -3,16 +3,19 @@ defmodule Openflow.Instruction.WriteActions do
alias __MODULE__ alias __MODULE__
def new(actions) do @type t :: %WriteActions{actions: [Openflow.Action.type()]}
%WriteActions{actions: actions}
end
@spec new(Openflow.Action.type() | [Openflow.Action.type()]) :: t()
def new(actions), do: %WriteActions{actions: actions}
@spec to_binary(t()) :: <<_::32, _::_*8>>
def to_binary(%WriteActions{actions: actions}) do def to_binary(%WriteActions{actions: actions}) do
actions_bin = Openflow.Action.to_binary(actions) actions_bin = Openflow.Action.to_binary(actions)
length = 8 + byte_size(actions_bin) length = 8 + byte_size(actions_bin)
<<3::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>> <<3::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>>
end end
@spec read(<<_::32, _::_*8>>) :: t()
def read(<<3::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do def read(<<3::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do
actions = Openflow.Action.read(actions_bin) actions = Openflow.Action.read(actions_bin)
%WriteActions{actions: actions} %WriteActions{actions: actions}

View file

@ -1,19 +1,31 @@
defmodule Openflow.Instruction.WriteMetadata do defmodule Openflow.Instruction.WriteMetadata do
defstruct(metadata: 0, metadata_mask: 0xFFFFFFFFFFFFFFFF) defstruct(value: 0, mask: 0xFFFFFFFFFFFFFFFF)
alias __MODULE__ alias __MODULE__
@type t :: %WriteMetadata{
value: 0..0xFFFFFFFFFFFFFFFF,
mask: 0..0xFFFFFFFFFFFFFFFF
}
@spec new(
value: 0..0xFFFFFFFFFFFFFFFF,
mask: 0..0xFFFFFFFFFFFFFFFF
) :: t()
def new(options) do def new(options) do
metadata = Keyword.get(options, :metadata, 0) %WriteMetadata{
metadata_mask = Keyword.get(options, :metadata_mask, 0xFFFFFFFFFFFFFFFF) value: options[:value] || 0,
%WriteMetadata{metadata: metadata, metadata_mask: metadata_mask} mask: options[:mask] || 0xFFFFFFFFFFFFFFFF
}
end end
def to_binary(%WriteMetadata{metadata: metadata, metadata_mask: metadata_mask}) do @spec to_binary(t()) :: <<_::192>>
<<2::16, 24::16, 0::size(4)-unit(8), metadata::64, metadata_mask::64>> def to_binary(%WriteMetadata{value: value, mask: mask}) do
<<2::16, 24::16, 0::size(4)-unit(8), value::64, mask::64>>
end end
def read(<<2::16, 24::16, _::size(4)-unit(8), metadata::64, metadata_mask::64>>) do @spec read(<<_::192>>) :: t()
%WriteMetadata{metadata: metadata, metadata_mask: metadata_mask} def read(<<2::16, 24::16, _::size(4)-unit(8), value::64, mask::64>>) do
%WriteMetadata{value: value, mask: mask}
end end
end end

View file

@ -0,0 +1,35 @@
defmodule OfpInstructionTest do
use ExUnit.Case
doctest Openflow
alias Openflow.Instruction.{
ApplyActions,
WriteActions,
ClearActions,
GotoTable,
WriteMetadata,
Meter,
Experimenter
}
describe "Openflow.Instruction" do
test "with all instructions parse and generate" do
instructions = [
ApplyActions.new([Openflow.Action.Output.new(:controller)]),
WriteActions.new([Openflow.Action.Output.new(5)]),
ClearActions.new(),
GotoTable.new(10),
WriteMetadata.new(value: 100, mask: 0xFFFFFFFFFFFFFFFF),
Meter.new(100),
Experimenter.new(0xCAFEBABE, "hogehoge"),
Experimenter.new(0xCAFEBABE)
]
instructions
|> Openflow.Instruction.to_binary()
|> Openflow.Instruction.read()
|> Kernel.==(instructions)
|> assert()
end
end
end