Skip to content

v1.2: Concurrent diffs with Differ - #71

Open
daniel-nichter wants to merge 15 commits into
mainfrom
v1.2
Open

daniel-nichter wants to merge 15 commits into
mainfrom
v1.2

Conversation

@daniel-nichter

@daniel-nichter daniel-nichter commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

v1.2 is based on #68. It adds:

  • Differ to allow concurrent diffs (i.e. no global/pkg vars that deep.Equal() uses)
  • Inline testing (see WithInlineTesting) shown below

Original:

if diff := deep.Eqaul(a, b); diff != nil {
    t.Error(diff)
}

New:

d := deep.NewDiffer()
if diff := d.Equal(a, b); diff != nil {
    t.Error(diff)
}

But then I realized I could do something I've been doing recently in another project:

d := deep.Testing(t)
d.Equal(a, b)

After many years of that boilerplate, I'm happy to get rid of it cleanly with this new Differ design.

There's also Equalf for custom error message formatting.

@daniel-nichter

Copy link
Copy Markdown
Member Author

@acorello Do you use Equal to compare two deltas? If not, I'll remove that part because once a feature exists, it can't be taken back and it has to be supported. But if you use it, I'll keep it.

I' m renaming stuff a bit that you can see in this PR.

@daniel-nichter daniel-nichter changed the title V1.2 v1.2 Oct 5, 2026
@daniel-nichter daniel-nichter changed the title v1.2 v1.2: Concurrent diffs with Differ Oct 5, 2026
@coveralls

coveralls commented Oct 13, 2026 •

Copy link
Copy Markdown

Coverage Status

Coverage is 100.0% — v1.2 into main. No base build found for main.

@daniel-nichter

daniel-nichter commented Oct 26, 2026 •

Copy link
Copy Markdown
Member Author

After using v1.2 in a real project, the original "ergonomics" weren't smooth enough. E.g. I can't type deep.NewDiffer(deep.WithInlineTesting(deep.InlineTesting{T: t})), probably can't even remember that. But deep.Testing(t) I can. Then I really wanted single inline Equal in most cases, but found sometimes I have []testCase, so I needed Equalf, which is why Diff didn't work: Difff (3 "f"s). (I know Go test adds subtest names to output, but sometimes I don't use subtests and use a list of test cases structs instead.) But then with Equal and Equalf, Diff no longer served a purpose--why have a Diff that's not reported when Equal returns the Diff too? So I made diff private, leaving only Equal and Equalf. Since this comes full circle the original design of this pkg almost 10 years ago, I guess it was meant to be. It's easier to add than to change or remove, so if Diff or other methods or things are truly needed/requested by many, maybe they'll be added.

Comment thread differ.go Outdated
}

c.equals(aVal, bVal, 0)
c.equals(aVal, bVal, 0) // full diff

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reckon you could put this in an else branch of the previous conditional, and remove the goto?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Done.

@acorello

Copy link
Copy Markdown

@acorello Do you use Equal to compare two deltas? If not, I'll remove [it]

Hey, thanks for asking. Just want to confirm I'm not using it.

@acorello

acorello commented Oct 31, 2026 •

Copy link
Copy Markdown

really wanted single inline Equal

I have enjoyed using rliebz/ghost to turn my assertions into readable one-liners, in small personal projects. Example:

g := ghost.New(t)

got, err := MyFunc()
g.NoError(err)

g.MustNot(be.Nil(got)) // Must calls `t.Fatal`
g.Should(be.Equal(got.SomeString, "my value")) // Should calls `t.Error`

I thought you may be interested if the desire to have a single inline Equal was about removing boilerplate.

The API consists of a handful of assertions on g: should, must, error, and their negations; all taking a Result as parameter, which drives the conditional t.Error and provides the messages.

Looking at your recent changes I started wondering whether to delegate the handling/wrapping of testing.T to an external library such as ghost, and implementing a side-car package returning the expected Result: eg g.Should(have.NoDiff(differ.Diff("foo", "bar")))).

I do anticipate anticipate one issue: is Result is a concrete type, that would force the side package to import it; if it was an interface one could be decoupled.
Also, it's not as short as the Equal one-liner calling t.Error. But seemed worth mentioning in case separation of concern, or building an eco-system, was something you had appetite for.

I'm not able to explore the design implications yet, but thought of sharing the idea.

Thanks for sharing your work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants