diff --git a/.gitignore b/.gitignore index 7686edf..ac0ab0f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,8 @@ priv/ _build vars.config rebar -deps/ \ No newline at end of file +deps/ + +*.eqc +.eqc-info +mix.lock diff --git a/Makefile b/Makefile index fa61451..573c93f 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,10 @@ clean: test: all rebar -v skip_deps=true eunit +# We assume Elixir and Quviq Quickcheck are installed +exunit: + MIX_EXS=test/elixir/mix.exs mix test + check-syntax: gcc -o nul -S ${CHK_SOURCES} diff --git a/test/elixir/mix.exs b/test/elixir/mix.exs new file mode 100644 index 0000000..cc930f0 --- /dev/null +++ b/test/elixir/mix.exs @@ -0,0 +1,45 @@ +# Copyright (C) 2002-2015 ProcessOne, SARL. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This mix module is used to configure project to run Elixir tests +# Elixir test can be run from project root using command: +# make exunit + +defmodule P1Xml.Mixfile do + use Mix.Project + + def project do + [app: :p1_xml, + version: "1.1.0", + elixir: "~> 1.1", + build_embedded: Mix.env == :prod, + start_permanent: Mix.env == :prod, + test_paths: ["test/elixir"], + deps: deps] + end + + def application do + [applications: [:logger], + mod: {:xml_app, []}] + end + + defp deps do + [{:p1_utils, + ~r//, # project is not semantically versioned + github: "processone/p1_utils", + compile: "rebar compile" + }, + {:eqc_ex, "~> 1.2.3"}] + end +end diff --git a/test/elixir/test_helper.exs b/test/elixir/test_helper.exs new file mode 100644 index 0000000..5194264 --- /dev/null +++ b/test/elixir/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +# Mix is starting application p1_xml automatically diff --git a/test/elixir/xml_test.exs b/test/elixir/xml_test.exs new file mode 100644 index 0000000..ecefbe7 --- /dev/null +++ b/test/elixir/xml_test.exs @@ -0,0 +1,112 @@ +# +# Test XML module with Quviq Quickcheck +# +# Copyright (C) 2002-2015 ProcessOne, SARL. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import EQC +import :eqc_gen +import :erlang, only: [list_to_binary: 1] + +defmodule XmlTest do + use ExUnit.Case + require Record + + Record.defrecord :xmlel, Record.extract(:xmlel, from: "include/xml.hrl") + +# test "generator" do +# sample(xml_el) +# end + + test "Can serialize arbitrary XML packets" do + assert :eqc.quickcheck(prop_can_serialize) + end + + test "Serialize and parse same XML packet" do + assert :eqc.quickcheck(prop_encode_decode) + end + + # Properties + # ========== + + def prop_can_serialize do + forall xml_chunk <- xml_el do + is_binary(:xml.element_to_binary(xml_chunk)) + end + end + + # Classical Quickcheck identify pattern: + def prop_encode_decode do + forall xml_chunk <- xml_el do + :xml_stream.parse_element(:xml.element_to_binary(xml_chunk)) == xml_chunk + end + end + + # Generators + # ========== + # + # Random small XML packets generator + # ---------------------------------- + + # TODO: We need to weight lowercase char much more that the rest + # An XML name cannot start with number of punctuation char + def xml_name do + [letter|list(letter_figure)] + end + + def letter do + oneof([choose(?A, ?Z), choose(?a, ?z)]) + end + + def letter_figure do + oneof([choose(?A, ?Z),choose(?a, ?z), choose(?0, ?9)]) + end + + def xml_attr do + let {key, val} <- {xml_name, xml_name} do + {list_to_binary(key), list_to_binary(val)} + end + end + + def xml_children(0) do + [] + end + + def xml_children(size) do + let name <- xml_name do + oneof([xml_children(0), + oneof([[{:xmlcdata, list_to_binary(name)}], + list(xml_child(div size, 3))])]) + end + end + + def xml_child(size) do + let {name, attrs} <- {xml_name, list(xml_attr)} do + xmlel(name: list_to_binary(name), + attrs: :lists.ukeysort(1, attrs), + children: xml_children(size)) + end + end + + def xml_el do + sized size do + let {tagname, attrs} <- {xml_name, list(xml_attr)} do + xmlel(name: list_to_binary(tagname), + attrs: :lists.ukeysort(1, attrs), + children: xml_children(size)) + end + end + end + +end