quality: Add test cases for instructions
This commit is contained in:
parent
b4c6f0822c
commit
a0ee397921
9 changed files with 120 additions and 43 deletions
|
|
@ -1,18 +1,31 @@
|
|||
defmodule Openflow.Instruction do
|
||||
def read(instruction_bin) do
|
||||
do_read([], instruction_bin)
|
||||
end
|
||||
@moduledoc """
|
||||
Openflow instruction codec handler
|
||||
"""
|
||||
|
||||
def to_binary(instructions) when is_list(instructions) do
|
||||
to_binary(<<>>, instructions)
|
||||
end
|
||||
@type instruction ::
|
||||
Openflow.Instruction.ApplyActions.t()
|
||||
| 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
|
||||
to_binary([instruction])
|
||||
end
|
||||
@spec read(<<_::32, _::_*8>>) :: [instruction()]
|
||||
def read(instruction_bin),
|
||||
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
|
||||
|
||||
@spec do_read([instruction()], <<_::_*8>>) :: [instruction()]
|
||||
defp do_read(acc, <<>>), do: Enum.reverse(acc)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
@spec to_binary(<<_::_*8>>) :: [instruction()]
|
||||
defp to_binary(acc, []), do: acc
|
||||
|
||||
defp to_binary(acc, [instruction | rest]) do
|
||||
|
|
|
|||
|
|
@ -3,16 +3,19 @@ defmodule Openflow.Instruction.ApplyActions do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
def new(actions) do
|
||||
%ApplyActions{actions: actions}
|
||||
end
|
||||
@type t :: %ApplyActions{actions: [Openflow.Action.type()]}
|
||||
|
||||
@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
|
||||
actions_bin = Openflow.Action.to_binary(actions)
|
||||
length = 8 + byte_size(actions_bin)
|
||||
<<4::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>>
|
||||
end
|
||||
|
||||
@spec read(<<_::64, _::_*8>>) :: t()
|
||||
def read(<<4::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do
|
||||
actions = Openflow.Action.read(actions_bin)
|
||||
%ApplyActions{actions: actions}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@ defmodule Openflow.Instruction.ClearActions do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
def new do
|
||||
%ClearActions{}
|
||||
end
|
||||
@type t :: %ClearActions{}
|
||||
|
||||
def to_binary(%ClearActions{}) do
|
||||
actions_bin = ""
|
||||
length = 8 + byte_size(actions_bin)
|
||||
<<5::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>>
|
||||
end
|
||||
@spec new() :: t()
|
||||
def new,
|
||||
do: %ClearActions{}
|
||||
|
||||
def read(<<5::16, _length::16, _::size(4)-unit(8), _::bytes>>) do
|
||||
%ClearActions{}
|
||||
end
|
||||
@spec to_binary(t()) :: <<_::64>>
|
||||
def to_binary(%ClearActions{}),
|
||||
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
|
||||
|
|
|
|||
|
|
@ -3,15 +3,19 @@ defmodule Openflow.Instruction.Experimenter do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
def new(exp_id, data \\ "") do
|
||||
%Experimenter{exp_id: exp_id, data: data}
|
||||
end
|
||||
@type t :: %Experimenter{exp_id: 0..0xFFFFFFFF, data: binary()}
|
||||
|
||||
@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
|
||||
length = 8 + byte_size(data)
|
||||
<<0xFFFF::16, length::16, exp_id::32, data::bytes>>
|
||||
end
|
||||
|
||||
@spec read(<<_::64, _::_*8>>) :: t()
|
||||
def read(<<0xFFFF::16, _::16, exp_id::32, data::bytes>>) do
|
||||
%Experimenter{exp_id: exp_id, data: data}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,15 +3,18 @@ defmodule Openflow.Instruction.GotoTable do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
def new(table_id) do
|
||||
%GotoTable{table_id: table_id}
|
||||
end
|
||||
@type t :: %GotoTable{table_id: 0..0xFF}
|
||||
|
||||
@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
|
||||
table_id_int = Openflow.Utils.get_enum(table_id, :table_id)
|
||||
<<1::16, 8::16, table_id_int::8, 0::size(3)-unit(8)>>
|
||||
end
|
||||
|
||||
@spec read(<<_::64>>) :: t()
|
||||
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)
|
||||
%GotoTable{table_id: table_id}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,18 @@ defmodule Openflow.Instruction.Meter do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
def new(meter_id) do
|
||||
%Meter{meter_id: meter_id}
|
||||
end
|
||||
@type t :: %Meter{meter_id: 0..0xFFFFFFFF}
|
||||
|
||||
@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
|
||||
meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id)
|
||||
<<6::16, 8::16, meter_id_int::32>>
|
||||
end
|
||||
|
||||
@spec read(<<_::64>>) :: t()
|
||||
def read(<<6::16, _::16, meter_id_int::32>>) do
|
||||
meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id)
|
||||
%Meter{meter_id: meter_id}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,19 @@ defmodule Openflow.Instruction.WriteActions do
|
|||
|
||||
alias __MODULE__
|
||||
|
||||
def new(actions) do
|
||||
%WriteActions{actions: actions}
|
||||
end
|
||||
@type t :: %WriteActions{actions: [Openflow.Action.type()]}
|
||||
|
||||
@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
|
||||
actions_bin = Openflow.Action.to_binary(actions)
|
||||
length = 8 + byte_size(actions_bin)
|
||||
<<3::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>>
|
||||
end
|
||||
|
||||
@spec read(<<_::32, _::_*8>>) :: t()
|
||||
def read(<<3::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do
|
||||
actions = Openflow.Action.read(actions_bin)
|
||||
%WriteActions{actions: actions}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,31 @@
|
|||
defmodule Openflow.Instruction.WriteMetadata do
|
||||
defstruct(metadata: 0, metadata_mask: 0xFFFFFFFFFFFFFFFF)
|
||||
defstruct(value: 0, mask: 0xFFFFFFFFFFFFFFFF)
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@type t :: %WriteMetadata{
|
||||
value: 0..0xFFFFFFFFFFFFFFFF,
|
||||
mask: 0..0xFFFFFFFFFFFFFFFF
|
||||
}
|
||||
|
||||
@spec new(
|
||||
value: 0..0xFFFFFFFFFFFFFFFF,
|
||||
mask: 0..0xFFFFFFFFFFFFFFFF
|
||||
) :: t()
|
||||
def new(options) do
|
||||
metadata = Keyword.get(options, :metadata, 0)
|
||||
metadata_mask = Keyword.get(options, :metadata_mask, 0xFFFFFFFFFFFFFFFF)
|
||||
%WriteMetadata{metadata: metadata, metadata_mask: metadata_mask}
|
||||
%WriteMetadata{
|
||||
value: options[:value] || 0,
|
||||
mask: options[:mask] || 0xFFFFFFFFFFFFFFFF
|
||||
}
|
||||
end
|
||||
|
||||
def to_binary(%WriteMetadata{metadata: metadata, metadata_mask: metadata_mask}) do
|
||||
<<2::16, 24::16, 0::size(4)-unit(8), metadata::64, metadata_mask::64>>
|
||||
@spec to_binary(t()) :: <<_::192>>
|
||||
def to_binary(%WriteMetadata{value: value, mask: mask}) do
|
||||
<<2::16, 24::16, 0::size(4)-unit(8), value::64, mask::64>>
|
||||
end
|
||||
|
||||
def read(<<2::16, 24::16, _::size(4)-unit(8), metadata::64, metadata_mask::64>>) do
|
||||
%WriteMetadata{metadata: metadata, metadata_mask: metadata_mask}
|
||||
@spec read(<<_::192>>) :: t()
|
||||
def read(<<2::16, 24::16, _::size(4)-unit(8), value::64, mask::64>>) do
|
||||
%WriteMetadata{value: value, mask: mask}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
35
test/ofp_instruction_test.exs
Normal file
35
test/ofp_instruction_test.exs
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue