├── .eqc_ci ├── EQC_CI_LICENCE.txt └── kv.erl /.eqc_ci: -------------------------------------------------------------------------------- 1 | {build,"erl -make"}. 2 | {test_path,"."}. 3 | -------------------------------------------------------------------------------- /EQC_CI_LICENCE.txt: -------------------------------------------------------------------------------- 1 | This file is an agreement between Quviq AB ("Quviq"), Sven Hultins 2 | Gata 9, Gothenburg, Sweden, and the committers to the github 3 | repository in which the file appears ("the owner"). By placing this 4 | file in a github repository, the owner agrees to the terms below. 5 | 6 | The purpose of the agreement is to enable Quviq AB to provide a 7 | continuous integration service to the owner, whereby the code in the 8 | repository ("the source code") is tested using Quviq's test tools, and 9 | the test results are made available on the web. The test results 10 | include test output, generated test cases, and a copy of the source 11 | code in the repository annotated with coverage information ("the test 12 | results"). 13 | 14 | The owner agrees that Quviq may run the tests in the source code and 15 | display the test results on the web, without obligation. 16 | 17 | The owner warrants that running the tests in the source code and 18 | displaying the test results on the web violates no laws, licences or other 19 | agreements. In the event of such a violation, the owner accepts full 20 | responsibility. 21 | 22 | The owner warrants that the source code is not malicious, and will not 23 | mount an attack on either Quviq's server or any other server--for 24 | example by taking part in a denial of service attack, or by attempting 25 | to send unsolicited emails. 26 | 27 | The owner warrants that the source code does not attempt to reverse 28 | engineer Quviq's code. 29 | 30 | Quviq reserves the right to exclude repositories that break this 31 | agreement from its continuous integration service. 32 | 33 | Any dispute arising from the use of Quviq's service will be resolved 34 | under Swedish law. 35 | -------------------------------------------------------------------------------- /kv.erl: -------------------------------------------------------------------------------- 1 | %% This file contains an implementation of a simple key-value store, 2 | %% and a state-machine specification of it. 3 | 4 | -module(kv). 5 | 6 | -include_lib("eqc/include/eqc.hrl"). 7 | -compile({parse_transform,eqc_cover}). 8 | -include_lib("eqc/include/eqc_statem.hrl"). 9 | 10 | -compile(export_all). 11 | 12 | %% The key-value store is managed by a server, and implemented as a binary tree. 13 | 14 | start() -> 15 | catch unregister(kv), 16 | register(kv,spawn_link(fun() -> server(leaf) end)). 17 | 18 | server(T) -> 19 | receive 20 | {insert,K,V} -> 21 | server(insert(K,V,T)); 22 | {lookup,K,Pid} -> 23 | Pid ! lookup(K,T), 24 | server(T) 25 | after 5000 -> 26 | %% Our server dies after 5 seconds of inactivity... just so we 27 | %% don't fill the memory with idle servers. 28 | ok 29 | end. 30 | 31 | insert(K,V,leaf) -> 32 | {node,leaf,K,V,leaf}; 33 | insert(K,V,{node,L,KN,_VN,R}) -> 34 | if K 35 | insert(K,V,L); 36 | K==KN -> 37 | {node,L,K,V,R}; 38 | K>KN -> 39 | insert(K,V,R) 40 | end. 41 | 42 | lookup(_,leaf) -> 43 | false; 44 | lookup(K,{node,L,KN,VN,R}) -> 45 | if K 46 | lookup(K,L); 47 | K==KN -> 48 | {K,VN}; 49 | K>KN -> 50 | lookup(K,R) 51 | end. 52 | 53 | %% State machine 54 | 55 | initial_state() -> 56 | []. 57 | 58 | %% insert 59 | 60 | insert(K,V) -> 61 | kv ! {insert,K,V}. 62 | 63 | insert_args(_) -> 64 | [key(),val()]. 65 | 66 | insert_next(S,_,[K,V]) -> 67 | lists:keystore(K,1,S,{K,V}). 68 | 69 | %% lookup 70 | 71 | lookup(K) -> 72 | kv ! {lookup,K,self()}, 73 | receive Msg -> 74 | Msg 75 | end. 76 | 77 | lookup_args(_) -> 78 | [key()]. 79 | 80 | lookup_post(S,[K],Res) -> 81 | eq(Res,lists:keyfind(K,1,S)). 82 | 83 | %% Generators 84 | 85 | key() -> 86 | nat(). 87 | 88 | val() -> 89 | nat(). 90 | 91 | %% Property 92 | 93 | prop_kv() -> 94 | ?FORALL(Cmds, commands(?MODULE), 95 | begin 96 | start(), 97 | {H, S, Res} = run_commands(?MODULE,Cmds), 98 | pretty_commands(?MODULE, Cmds, {H, S, Res}, 99 | aggregate(command_names(Cmds), 100 | ?IMPLIES(Res/=precondition, 101 | Res == ok))) 102 | end). 103 | --------------------------------------------------------------------------------