From 08b3c65c79f360ed6893b75a13dbe53b39cb7185 Mon Sep 17 00:00:00 2001 From: ieQu1 <99872536+ieQu1@users.noreply.github.com> Date: Thu, 2 May 2024 17:24:58 +0200 Subject: [PATCH 1/7] Add facility for diffing lists --- include/common.hrl | 4 + include/snabbkaffe.hrl | 10 ++ src/snabbkaffe.erl | 14 ++- src/snabbkaffe_diff.erl | 225 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 src/snabbkaffe_diff.erl diff --git a/include/common.hrl b/include/common.hrl index cc08a4e..24be143 100644 --- a/include/common.hrl +++ b/include/common.hrl @@ -13,4 +13,8 @@ -define(snk_span, '$span'). -endif. +-ifndef(snk_deferred_assert). +-define(snk_deferred_assert, 'Assertion failed'). +-endif. + -endif. diff --git a/include/snabbkaffe.hrl b/include/snabbkaffe.hrl index 0aa9325..d1e25bc 100644 --- a/include/snabbkaffe.hrl +++ b/include/snabbkaffe.hrl @@ -4,4 +4,14 @@ -include("trace.hrl"). -include("test_macros.hrl"). +-define(defer_assert(BODY), + (fun() -> + try BODY + catch + EC:Err:Stack -> + ?tp(error, ?snk_deferred_assert, #{EC => Err, stacktrace => Stack}) + end, + ok + end)()). + -endif. diff --git a/src/snabbkaffe.erl b/src/snabbkaffe.erl index b0ef361..6298af4 100644 --- a/src/snabbkaffe.erl +++ b/src/snabbkaffe.erl @@ -1,4 +1,4 @@ -%% Copyright 2021-2023 snabbkaffe contributors +%% Copyright 2021-2024 snabbkaffe contributors %% Copyright 2019-2020 Klarna Bank AB %% %% Licensed under the Apache License, Version 2.0 (the "License"); @@ -762,7 +762,8 @@ run_stage(Run, Config) -> -spec check_stage(trace_specs(Result), Result, trace(), run_config()) -> boolean() | {error, _}. check_stage(Fun, Result, Trace, Config) when is_function(Fun) -> check_stage([{"check stage", Fun}], Result, Trace, Config); -check_stage(Specs, Result, Trace, Config) -> +check_stage(Specs0, Result, Trace, Config) -> + Specs = [{"Deferred asserts", fun check_deferred/1} | Specs0], Failed = [Spec || Spec <- Specs, not run_trace_spec(Spec, Result, Trace, Config)], case Failed of [] -> @@ -773,6 +774,15 @@ check_stage(Specs, Result, Trace, Config) -> {error, check_stage_failed} end. +check_deferred(Trace) -> + case ?of_kind(?snk_deferred_assert, Trace) of + [] -> + true; + Fails -> + _ = [logger:error(I) || I <- Fails], + false + end. + %% @private run_trace_spec(Spec, Result, Trace, Config) -> case Spec of diff --git a/src/snabbkaffe_diff.erl b/src/snabbkaffe_diff.erl new file mode 100644 index 0000000..18e6319 --- /dev/null +++ b/src/snabbkaffe_diff.erl @@ -0,0 +1,225 @@ +%% Copyright 2024 snabbkaffe contributors +%% +%% 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. + +%% @doc This module contains helper functions for diffing large Erlang lists. +%% +%% It tries to limit the amount of information returned to the user. +-module(snabbkaffe_diff). + +%% API: +-export([diff_lists/3, format/1, assert_lists_eq/2, assert_lists_eq/3]). + +-export_type([cfun/2, options/0, diff/2]). + +%%================================================================================ +%% Type declarations +%%================================================================================ + +-record(context, + { size :: non_neg_integer() + , n = 0 :: non_neg_integer() + , head :: queue:queue(A) + , tail :: queue:queue(A) + }). + +-type context(_A) :: #context{}. + +-type diff(A, B) :: {extra, B} | {missing, A} | {changed, A, B} | {ctx, B} | binary(). + +-type diff_(A, B) :: {extra, B} | {missing, A} | {changed, A, B} | {ctx, context(A), context(B)}. + +-record(s, + { ctx1 :: context(A) + , ctx2 :: context(B) + , n_failures = 0 :: non_neg_integer() + , failures :: context([diff_(A, B)]) + , window :: pos_integer() + }). + +-type cfun(A, B) :: fun((A, B) -> boolean()). + +-type options() :: + #{ compare_fun => cfun(_A, _B) % Function used for element comparision + , max_failures => non_neg_integer() % Limit the number differences returned + , context => non_neg_integer() % Add N elements around each difference + , window => pos_integer() % Look ahead when detecting skips + }. + +%%================================================================================ +%% API functions +%%================================================================================ + +-spec assert_lists_eq([_A], [_B]) -> ok. +assert_lists_eq(A, B) -> + assert_lists_eq(A, B, #{}). + +-spec assert_lists_eq([_A], [_B], options()) -> ok. +assert_lists_eq(A, B, Options) -> + case diff_lists(Options, A, B) of + [] -> + ok; + Fails -> + logger:error("Lists are not equal:~n~s", [format(Fails)]), + error(lists_not_equal) + end. + +-spec format(diff(_A, _B) | [diff(_A, _B)]) -> iolist(). +format({ctx, Bin}) when is_binary(Bin) -> + <>; +format({ctx, Term}) -> + io_lib:format( " ~9999p~n" + , [Term] + ); +format({extra, Term}) -> + io_lib:format( "!! ++ ~9999p~n" + , [Term] + ); +format({missing, Term}) -> + io_lib:format( "!! -- ~9999p~n" + , [Term] + ); +format({changed, T1, T2}) -> + io_lib:format( "!! - ~9999p~n" + "!! + ~9999p~n" + , [T1, T2] + ); +format(L) when is_list(L) -> + lists:map(fun format/1, L). + +-spec diff_lists(options(), [A], [B]) -> [diff(A, B)]. +diff_lists(Custom, A, B) -> + Default = #{ compare_fun => fun(X, Y) -> X =:= Y end + , context => 5 + , max_failures => 10 + , window => 100 + }, + #{ compare_fun := CFun + , context := CtxSize + , max_failures := MaxFailures + , window := Window + } = maps:merge(Default, Custom), + S = go(CFun, A, B, + #s{ ctx1 = ctx_new(CtxSize) + , ctx2 = ctx_new(CtxSize) + , failures = ctx_new(MaxFailures) + , window = Window + }), + #s{ctx1 = Ctx1, ctx2 = Ctx2, n_failures = NFails, failures = Fails0} = S, + Fails = lists:flatten([ctx_to_list(Fails0), {ctx, Ctx1, Ctx2}]), + case NFails of + 0 -> + []; + _ -> + lists:flatmap(fun(Bin) when is_binary(Bin) -> [{ctx, Bin}]; + ({ctx, _C1, C2}) -> [{ctx, I} || I <- ctx_to_list(C2)]; + (Diff) -> [Diff] + end, + Fails) + end. + +%%================================================================================ +%% Internal functions +%%================================================================================ + +go(_CFun, [], [], S) -> + S; +go(CFun, [Ha | A], [], S0) -> + S = fail({missing, Ha}, S0), + go(CFun, A, [], S); +go(CFun, [], [Hb | B], S0) -> + S = fail({extra, Hb}, S0), + go(CFun, [], B, S); +go(CFun, [Ha | A], [Hb | B], S0 = #s{ctx1 = Ctx1, ctx2 = Ctx2}) -> + case CFun(Ha, Hb) of + true -> + S = S0#s{ ctx1 = ctx_push(Ha, Ctx1) + , ctx2 = ctx_push(Hb, Ctx2) + }, + go(CFun, A, B, S); + false -> + {S, A1, B1} = detect_fail(CFun, [Ha | A], [Hb | B], S0), + go(CFun, A1, B1, S) + end. + +fail(Fail, S = #s{n_failures = NFailures, failures = Fails, ctx1 = Ctx1, ctx2 = Ctx2}) -> + S#s{ n_failures = NFailures + 1 + , failures = ctx_push([{ctx, Ctx1, Ctx2}, Fail], Fails) + , ctx1 = ctx_drop(Ctx1) + , ctx2 = ctx_drop(Ctx2) + }. + +detect_fail(CFun, [Ha | A], [Hb | B], S0 = #s{window = Window}) -> + %% Extremely stupid greedy algorithm is used here. + ScoreA = score(CFun, Ha, B, Window), + ScoreB = score(CFun, Hb, A, Window), + if ScoreA =:= ScoreB -> + { fail({changed, Ha, Hb}, S0) + , A + , B + }; + ScoreA > ScoreB -> + { fail({extra, Hb}, S0) + , [Ha | A] + , B + }; + ScoreA < ScoreB -> + { fail({missing, Ha}, S0) + , A + , [Hb | B] + } + end. + +-spec score(cfun(A, B), A, [B], non_neg_integer()) -> non_neg_integer(). +score(_CFun, _A, L, W) when L =:= []; W =< 0 -> + 0; +score(CFun, A, [B | L], W) -> + case CFun(A, B) of + true -> 1; + false -> score(CFun, A, L, W - 1) + end. + +%% Context management: + +ctx_new(Size) -> + #context{ size = Size + , head = queue:new() + , tail = queue:new() + }. + +ctx_push(A, Ctx = #context{size = Size, n = N, head = H0}) when N =:= Size - 1 -> + {H, T} = queue:split(trunc(Size / 2), queue:in(A, H0)), + Ctx#context{ n = N + 1 + , head = H + , tail = T + }; +ctx_push(A, Ctx = #context{size = Size, n = N, head = H}) when N < Size -> + Ctx#context{ n = N + 1 + , head = queue:in(A, H) + }; +ctx_push(A, Ctx = #context{n = N, tail = T0}) -> + {_, T1} = queue:out(T0), + T = queue:in(A, T1), + Ctx#context{ n = N + 1 + , tail = T + }. + +ctx_to_list(#context{size = Size, n = N, head = Hd, tail = Tl}) when N =< Size -> + queue:to_list(Hd) ++ queue:to_list(Tl); +ctx_to_list(#context{size = Size, n = N, head = Hd, tail = Tl}) -> + NSkipped = N - Size, + Placeholder = iolist_to_binary(io_lib:format("... omitted ~p elements ...", [NSkipped])), + queue:to_list(Hd) ++ [Placeholder | queue:to_list(Tl)]. + +ctx_drop(#context{size = Size}) -> + ctx_new(Size). From c87bc6ded074106a448195555aac28fc3f7af45f Mon Sep 17 00:00:00 2001 From: ieQu1 <99872536+ieQu1@users.noreply.github.com> Date: Thu, 2 May 2024 17:25:22 +0200 Subject: [PATCH 2/7] Fix dialyzer errors --- src/snabbkaffe.erl | 2 +- src/snabbkaffe_collector.erl | 6 +++--- src/snabbkaffe_nemesis.erl | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/snabbkaffe.erl b/src/snabbkaffe.erl index 6298af4..5c3bb53 100644 --- a/src/snabbkaffe.erl +++ b/src/snabbkaffe.erl @@ -124,7 +124,7 @@ -export_type([ kind/0, timestamp/0, event/0, timed_event/0, trace/0 , maybe_pair/0, maybe/1, metric/0, run_config/0, predicate/0 - , predicate2/0, trace_spec/1, trace_specs/1 + , predicate2/0, trace_spec/1, trace_specs/1, filter/0 ]). %%==================================================================== diff --git a/src/snabbkaffe_collector.erl b/src/snabbkaffe_collector.erl index 5c25fe5..cc0ada3 100644 --- a/src/snabbkaffe_collector.erl +++ b/src/snabbkaffe_collector.erl @@ -1,4 +1,4 @@ -%% Copyright 2021-2023 snabbkaffe contributors +%% Copyright 2021-2024 snabbkaffe contributors %% Copyright 2019-2020 Klarna Bank AB %% %% Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,7 +44,7 @@ -export([ do_forward_trace/1 ]). --export_type([async_action/0, subscription/0]). +-export_type([async_action/0, subscription/0, datapoints/0]). -define(SERVER, ?MODULE). @@ -116,7 +116,7 @@ get_stats() -> gen_server:call(?SERVER, get_stats, infinity). %% NOTE: Concuerror only supports `Timeout=0' --spec flush_trace() -> snabbkaffe:timed_trace(). +-spec flush_trace() -> snabbkaffe:trace(). flush_trace() -> {ok, Trace} = gen_server:call(?SERVER, flush_trace, infinity), Trace. diff --git a/src/snabbkaffe_nemesis.erl b/src/snabbkaffe_nemesis.erl index befe90d..9d802c5 100644 --- a/src/snabbkaffe_nemesis.erl +++ b/src/snabbkaffe_nemesis.erl @@ -1,5 +1,5 @@ -%% Copyright 2019-2020, 2022 Klarna Bank AB -%% Copyright 2021 snabbkaffe contributors +%% Copyright 2021-2024 snabbkaffe contributors +%% Copyright 2019-2020 Klarna Bank AB %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -91,7 +91,7 @@ -record(fault, { reference :: reference() , predicate :: snabbkaffe:predicate() - , scenario :: snabbkaffe:fault_scenario() + , scenario :: fault_scenario() , reason :: term() }). From 69fcfe5180815c694f1a764762124affab8ca8c4 Mon Sep 17 00:00:00 2001 From: ieQu1 <99872536+ieQu1@users.noreply.github.com> Date: Fri, 3 May 2024 00:51:53 +0200 Subject: [PATCH 3/7] Support streams in snabbkaffe_diff --- src/snabbkaffe.erl | 7 ++----- src/snabbkaffe_diff.erl | 27 +++++++++++++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/snabbkaffe.erl b/src/snabbkaffe.erl index 5c3bb53..2649be2 100644 --- a/src/snabbkaffe.erl +++ b/src/snabbkaffe.erl @@ -776,11 +776,8 @@ check_stage(Specs0, Result, Trace, Config) -> check_deferred(Trace) -> case ?of_kind(?snk_deferred_assert, Trace) of - [] -> - true; - Fails -> - _ = [logger:error(I) || I <- Fails], - false + [] -> true; + _ -> false end. %% @private diff --git a/src/snabbkaffe_diff.erl b/src/snabbkaffe_diff.erl index 18e6319..7581bf6 100644 --- a/src/snabbkaffe_diff.erl +++ b/src/snabbkaffe_diff.erl @@ -26,6 +26,12 @@ %% Type declarations %%================================================================================ +%% Stream can be either an ordinary list, or an improper list with +%% function as a tail, that produces more elements, a.k.a stream. +%% +%% WARNING: the user must make sure streams are finite! +-type stream(A) :: maybe_improper_list(A, [] | fun(() -> stream(A))). + -record(context, { size :: non_neg_integer() , n = 0 :: non_neg_integer() @@ -54,24 +60,25 @@ , max_failures => non_neg_integer() % Limit the number differences returned , context => non_neg_integer() % Add N elements around each difference , window => pos_integer() % Look ahead when detecting skips + , comment => term() }. %%================================================================================ %% API functions %%================================================================================ --spec assert_lists_eq([_A], [_B]) -> ok. +-spec assert_lists_eq(stream(_A), stream(_B)) -> ok. assert_lists_eq(A, B) -> assert_lists_eq(A, B, #{}). --spec assert_lists_eq([_A], [_B], options()) -> ok. +-spec assert_lists_eq(stream(_A), stream(_B), options()) -> ok. assert_lists_eq(A, B, Options) -> case diff_lists(Options, A, B) of [] -> ok; Fails -> logger:error("Lists are not equal:~n~s", [format(Fails)]), - error(lists_not_equal) + error({lists_not_equal, maps:get(comment, Options, "")}) end. -spec format(diff(_A, _B) | [diff(_A, _B)]) -> iolist(). @@ -97,7 +104,7 @@ format({changed, T1, T2}) -> format(L) when is_list(L) -> lists:map(fun format/1, L). --spec diff_lists(options(), [A], [B]) -> [diff(A, B)]. +-spec diff_lists(options(), stream(A), stream(B)) -> [diff(A, B)]. diff_lists(Custom, A, B) -> Default = #{ compare_fun => fun(X, Y) -> X =:= Y end , context => 5 @@ -132,6 +139,10 @@ diff_lists(Custom, A, B) -> %% Internal functions %%================================================================================ +go(CFun, Fa, B, S) when is_function(Fa, 0) -> + go(CFun, Fa(), B, S); +go(CFun, A, Fb, S) when is_function(Fb, 0) -> + go(CFun, A, Fb(), S); go(_CFun, [], [], S) -> S; go(CFun, [Ha | A], [], S0) -> @@ -180,8 +191,12 @@ detect_fail(CFun, [Ha | A], [Hb | B], S0 = #s{window = Window}) -> } end. --spec score(cfun(A, B), A, [B], non_neg_integer()) -> non_neg_integer(). -score(_CFun, _A, L, W) when L =:= []; W =< 0 -> +-spec score(cfun(A, B), A, stream(B), non_neg_integer()) -> non_neg_integer(). +score(_CFun, _A, _, W) when W =< 0 -> + 0; +score(CFun, A, Fun, W) when is_function(Fun) -> + score(CFun, A, Fun(), W); +score(_CFun, _A, [], _W) -> 0; score(CFun, A, [B | L], W) -> case CFun(A, B) of From 2808ea5d2cc4bc1b7d7f0d76aeabf13f31d717e7 Mon Sep 17 00:00:00 2001 From: ieQu1 <99872536+ieQu1@users.noreply.github.com> Date: Sat, 4 May 2024 19:01:15 +0200 Subject: [PATCH 4/7] Update documentation --- CHANGELOG.md | 7 +++++++ doc/src/extract_tests | 15 ++++----------- doc/src/fault_injection.md | 2 -- doc/src/offline_analysis.md | 9 +++++++-- doc/src/running.md | 30 +++++++++++++++++++++++++----- doc/src/scheduling_injection.md | 2 -- doc/src/waiting_events.md | 2 -- include/common.hrl | 7 ++++--- 8 files changed, 47 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 039faff..d85c18f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 1.0.10 + +### Features + +- Added new module `snabbkaffe_diff` that helps to compare large lists +- Added a new macro `?defer_assert(...)` that allows the run stage to continue after encountering a failure, and fails the testcase in the check stage instead. + ## 1.0.9 ### Features - Now it is possible to redefine what `?snk_kind` macro translates to in prod mode by defining `SNK_PROD_KIND` macro. diff --git a/doc/src/extract_tests b/doc/src/extract_tests index f665c3d..94a2432 100755 --- a/doc/src/extract_tests +++ b/doc/src/extract_tests @@ -1,16 +1,16 @@ -#!/usr/bin/awk -f +#!/usr/bin/gawk -f # Poor man's TANGLE BEGIN { print "%% Generated code, do not edit" + # print gensub(/(.+)\.md/, "\\1", FILENAME) + print "-module(" gensub(/.*\/([^/]+)\.md/, "\\1_example", 1, ARGV[1]) ")." printLine = 0 isSrcBlockStart = 0 - isEmpty = 1 } { - if (isSrcBlockStart && ($0 ~ /_test_?\(\) *->$/ || $0 ~ /^-module/)) { + if (isSrcBlockStart && ($0 ~ /_test_?\(\) *->$/ || $0 ~ /^-include/)) { # Beginning of the src block - isEmpty = 0 printLine = 1 } else if ($0 ~ /^``` *$/) { # End of the src block @@ -22,10 +22,3 @@ BEGIN { isSrcBlockStart = $0 ~ /^```erlang/ } - -END { - # Generate a dummy module if nothing has been extracted from the file: - if (isEmpty) { - system("echo \"-module($(basename " FILENAME " .md)_example).\"") - } -} diff --git a/doc/src/fault_injection.md b/doc/src/fault_injection.md index 80febab..46fe853 100644 --- a/doc/src/fault_injection.md +++ b/doc/src/fault_injection.md @@ -14,8 +14,6 @@ Note: injected crashes are global, they work on the remote nodes. Example: ```erlang --module(fault_injection_example). - -include_lib("snabbkaffe/include/snabbkaffe.hrl"). -include_lib("eunit/include/eunit.hrl"). diff --git a/doc/src/offline_analysis.md b/doc/src/offline_analysis.md index 5bdbb69..a5660bf 100644 --- a/doc/src/offline_analysis.md +++ b/doc/src/offline_analysis.md @@ -14,8 +14,6 @@ It finds pairs of events matching "cause" and "effect" patterns, and verifies th For example, the following test verifies that every received message eventually gets processed: ```erlang --module(offline_analysis_example). - -include_lib("snabbkaffe/include/snabbkaffe.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -260,3 +258,10 @@ projection_test() -> `snabbkaffe:unique(Trace)` will raise an exception of some event in the trace is repeated. It ignores the timestamps. + +## snabbkaffe_diff + +`snabbkaffe_diff` is a helper module that contains routines for comparison of Erlang terms (currently it focuses on the lists). + +Functions such as `snabbkaffe_diff:assert_lists_eq/3` and `snabbkaffe_diff:diff_lists/3` help to find and highlight differences between two large lists. +They try to provide the context while limiting the amount of logs printed when the number of differences is large. diff --git a/doc/src/running.md b/doc/src/running.md index f194a5d..06daa6f 100644 --- a/doc/src/running.md +++ b/doc/src/running.md @@ -7,15 +7,13 @@ Most of the work is done by `?check_trace` macro, which can be placed inside eun In the most basic form, Snabbkaffe tests look like this: ```erlang --module(running_example). - --compile(nowarn_export_all). --compile(export_all). - -include_lib("proper/include/proper.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). +-compile(nowarn_export_all). +-compile(export_all). + basic_test() -> ?check_trace( %% Run stage: @@ -162,3 +160,25 @@ simple_prop() -> ``` It is equivalent to the previous example. + +## Deferring asserts + +Snabbkaffe provides a useful macro `?defer_assert(BODY)` that allows the run stage to continue after encountering an error. +This is useful for checking multiple properties at once. + +Deferred assertions that fail instead are reported as error in the check stage: + +```erlang +deferred_assertion_test() -> + ?assertError( + _, + ?check_trace( + begin + ?defer_assert(?assert(false, "First assert")), + ?defer_assert(?assert(false, "Second assert")) + end, + []) + ). +``` + +In the above example both asserts are executed, the run stage succeeds, while the check stage fails. diff --git a/doc/src/scheduling_injection.md b/doc/src/scheduling_injection.md index 1bfa713..a68ceca 100644 --- a/doc/src/scheduling_injection.md +++ b/doc/src/scheduling_injection.md @@ -22,8 +22,6 @@ Parameters: Example: ```erlang --module(scheduling_injection_example). - -include_lib("eunit/include/eunit.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). diff --git a/doc/src/waiting_events.md b/doc/src/waiting_events.md index 4703b79..bf71d98 100644 --- a/doc/src/waiting_events.md +++ b/doc/src/waiting_events.md @@ -14,8 +14,6 @@ In the simplest case, `?block_until` macro can be used. As the name suggests, it blocks execution of the testcase until an event matching a pattern is emitted: ```erlang --module(waiting_events_example). - -include_lib("eunit/include/eunit.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). diff --git a/include/common.hrl b/include/common.hrl index 24be143..a666e90 100644 --- a/include/common.hrl +++ b/include/common.hrl @@ -13,8 +13,9 @@ -define(snk_span, '$span'). -endif. --ifndef(snk_deferred_assert). --define(snk_deferred_assert, 'Assertion failed'). --endif. +%% Redefining this macro should be impossible, because when snabbkaffe +%% library is compiled with different settings, it would lose the +%% events. +-define(snk_deferred_assert, 'Deferred assertion failed'). -endif. From 3e458d754b52b4b51ce1c1d87c7a0bf269f52fd9 Mon Sep 17 00:00:00 2001 From: ieQu1 <99872536+ieQu1@users.noreply.github.com> Date: Sat, 4 May 2024 19:02:09 +0200 Subject: [PATCH 5/7] Apply review remarks --- src/snabbkaffe_diff.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/snabbkaffe_diff.erl b/src/snabbkaffe_diff.erl index 7581bf6..b1c4d48 100644 --- a/src/snabbkaffe_diff.erl +++ b/src/snabbkaffe_diff.erl @@ -85,20 +85,20 @@ assert_lists_eq(A, B, Options) -> format({ctx, Bin}) when is_binary(Bin) -> <>; format({ctx, Term}) -> - io_lib:format( " ~9999p~n" + io_lib:format( " ~0p~n" , [Term] ); format({extra, Term}) -> - io_lib:format( "!! ++ ~9999p~n" + io_lib:format( "!! ++ ~0p~n" , [Term] ); format({missing, Term}) -> - io_lib:format( "!! -- ~9999p~n" + io_lib:format( "!! -- ~0p~n" , [Term] ); format({changed, T1, T2}) -> - io_lib:format( "!! - ~9999p~n" - "!! + ~9999p~n" + io_lib:format( "!! - ~0p~n" + "!! + ~0p~n" , [T1, T2] ); format(L) when is_list(L) -> From b0468e8026a3cae657237353c5c0f44d2777f6bd Mon Sep 17 00:00:00 2001 From: ieQu1 <99872536+ieQu1@users.noreply.github.com> Date: Sun, 5 May 2024 12:34:18 +0200 Subject: [PATCH 6/7] Quote 'maybe' type --- .github/workflows/ci.yml | 12 +++++++++--- CHANGELOG.md | 3 +++ src/snabbkaffe.erl | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81739cf..c5272c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,13 +13,19 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - otp: [25.2, 21.3.8.21] + otp: [27, 21] container: image: erlang:${{ matrix.otp }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - name: Fix repository ownership + run: git config --global --add safe.directory /__w/snabbkaffe/snabbkaffe - name: Compile and run tests - run: make + run: | + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y gawk + make - name: Test with concuerror run: make concuerror_test diff --git a/CHANGELOG.md b/CHANGELOG.md index d85c18f..644e6f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ - Added new module `snabbkaffe_diff` that helps to compare large lists - Added a new macro `?defer_assert(...)` that allows the run stage to continue after encountering a failure, and fails the testcase in the check stage instead. +### Fixes +- Quote a new reserved word (`maybe`) for compatibility with OTP27 + ## 1.0.9 ### Features - Now it is possible to redefine what `?snk_kind` macro translates to in prod mode by defining `SNK_PROD_KIND` macro. diff --git a/src/snabbkaffe.erl b/src/snabbkaffe.erl index 2649be2..508459d 100644 --- a/src/snabbkaffe.erl +++ b/src/snabbkaffe.erl @@ -101,7 +101,7 @@ -type maybe_pair() :: {pair, timed_event(), timed_event()} | {unmatched_cause | unmatched_effect, timed_event()}. --type maybe(A) :: {just, A} | nothing. +-type 'maybe'(A) :: {just, A} | nothing. -type run_config() :: #{ bucket => integer() @@ -123,7 +123,7 @@ | [trace_spec(Result) | {string(), trace_spec(Result)}]. -export_type([ kind/0, timestamp/0, event/0, timed_event/0, trace/0 - , maybe_pair/0, maybe/1, metric/0, run_config/0, predicate/0 + , maybe_pair/0, 'maybe'/1, metric/0, run_config/0, predicate/0 , predicate2/0, trace_spec/1, trace_specs/1, filter/0 ]). From 127e18e8aeceaea9c389a53d50c190ef0e34cfa9 Mon Sep 17 00:00:00 2001 From: ieQu1 <99872536+ieQu1@users.noreply.github.com> Date: Sun, 5 May 2024 13:50:14 +0200 Subject: [PATCH 7/7] Add nowarn_update_literal pragma to common.hrl --- include/common.hrl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/common.hrl b/include/common.hrl index a666e90..ead7e95 100644 --- a/include/common.hrl +++ b/include/common.hrl @@ -18,4 +18,6 @@ %% events. -define(snk_deferred_assert, 'Deferred assertion failed'). +-compile(nowarn_update_literal). + -endif.