My "unit test" fails

I am trying to write an “unit test” by using an assert macro that was suggested in another topic some time ago.

Here’s my code so far:

\widget $assert.equal(name, param1, param2)
<<name>>
<%if [<param1>match<param2>] %>
OK
<%else%>
FAIL
<!-- Display variables -->
param1=<<param1>>, param2=<<param2>>
<%endif%>
\end

\function .get_line_number_from_str(str, line_number)
[<str>splitregexp[\n]first<line_number>last[1]]
\end

<$vars text="line1\n\line2\nline3" line_number="1" line1="line1" >
    <$assert.equal name="Test that line <<line_number>> from '<<text>>' == '<<line1>>'" param1=<<.get_line_number_from_str <<text>>,<<line_number>>>> param2=<<line1>> />
</$vars>

which renders to:

Test that line 1 from 'line1\n\line2\nline3' == 'line1' FAIL param1=<<text, param2=

Besides needing help with fixing the test, I’d like suggestions on the following issues:

  • lack of syntax highlight when writing code logic wikitext (I use the CodeMirror vanilla plugin) is extremely painful. how do you write complex functions/macros/filters/whatever without it? the above code snippet is already big enough to overflow my mental model
  • lack of a built-in wikitext linter that could produce relevant and helpful error messages is even more painful. By looking at my failed unit test output, I could assume the problem might be a syntax error (like the typical misuse of dreaded brackets for example), but I’m no wikitext guru, I’m a learning starter and I could really use some instructive feedback of a tool as alternative to nagging the more experienced audience of this forum every time a piece of my code doesn’t work[1]
  1. I truly hope that the “rantish” tone of this post is for a good cause - I have to “scream” my pain, because the gap between hello world level wikitext code and a minimalistic wikitext snippet that is practically useful seems to be a bit too big for my taste. And the resort to unit testing is an attempt to bridge this gap and boost my learning “independence” - as in so I could advance faster on my own, by relying more on “best practices” rather than creating too many help request posts in the forum.

Your test fails because of this parameter assignment in your assertion.

param1=<<.get_line_number_from_str <<text>>,<<line_number>>>>

It is not possible to assign parametes with a macro call that uses nested closing angle brackets. The parser can not handle this.


You should also use <$let instead of <$vars

Visual Studio Code + TiddlyWiki5 Syntax - Visual Studio Marketplace

For debugging, the log and action-log widgets are extremely helpful.

That looks like a weird mashup of attribute syntax and filter syntax that I have never seen before.

This might be closer to what you need:
param1={{{ [.get_line_number_from_str<text>,<linenumber>] }}}

This is my limited syntax knowledge + my clumsy thought process “ported” to wikitext :rofl: :

  • variables in wikitext can be referenced with << >> syntax
  • macros too
  • functions are an evolution of macros, thus it should be possible to use the same << >> syntax

As your filter transclusion example shows, the third assumption was false?

Functions can be referenced using << >> syntax. But not inside a widget call, with nested parameters. So it depends on the context.

This thread may be helpful: Those Pesky Brackets

This kind of feedback I’d like to get from a linter :+1:

Having a built in minimalist widget toolkit for pure unit testing (not visual interactive tests, that can be done with TestCase widget), with a few examples showcasing best practices would be even better. And it would exclude the technical debt if in the future such a thing would make it to vanilla and one would need to rewrite lots of “custom” unit tests.

I would suggest not thinking of that way as they are quite different other than both being variables.

See https://tiddlywiki.com/#Functions for usage examples.

I suppose it’s because of this:

$vars differs from the $let in that variables cannot interfere with the evaluation of other variables within the same $vars .

I have modified my code to include these fixes, now it looks like

\widget $assert.equal(name, param1, param2)
<<name>>
<%if [<param1>match<param2>] %>
OK
<%else%>
FAIL
<!-- Display variables -->
param1=<<param1>>, param2=<<param2>>
<%endif%>
\end

\function .get_line_number_from_str(str, line_number)
[<str>splitregexp[\n]first<line_number>last[1]]
\end

<$let text="line1\n\line2\nline3" line_number="1" line1="line1" expected_line={{{ [.get_line_number_from_str<text>,<line_number>] }}}>
    expected line = <<expected_line>>
		<br /><br />
    <$assert.equal name="Test that line <<line_number>> from '<<text>>' == '<<line1>>'" param1=<<expected_line>> param2=<<line1>> />
</$let>

and renders to:

expected line = line1\n\line2\nline3

Test that line 1 from 'line1\n\line2\nline3' == 'line1' FAIL param1=line1\n\line2\nline3, param2=line1

In my function, if I replace splitregexp with split - the test passes. I suppose this means that in my declaration of text var, the hardcoded \n is treated literally rather than like a real newline in a tiddler text field? How can I prepare a “good” hardcoded multiline string in a variable? In this unit test I want to avoid creating a fixture temporary tiddler and using its content for testing purposes (but that will be the next step, since ultimately I’ll be picking lines of text from tiddler text fields anyway).

I have to agree with the ‘rantish character’ of your post.

My experience: when you bump into syntax problems again and again, you switch to a bottom-up programming style (indeed unit-testing). You make a small box and when it finally works, you try to put it into a larger one, and so on.

Bu then, to use a metaphore, TW boxes have several colors, and for each combination of colors of a smaller and a bigger box, you have to find the right way to put them together, mostly by trial and error. This is indeed very frustrating and forces you in the bottom-up approach even more.

Therefore, in my opinion, we need a how-to guide that focuses on the bottom-up programming style resulting from TW’s many syntax tripwires (and lack of useful error messages). So it should answer questions like ‘I have made a simple procedure that works. How can I incorporate it in another procedure? In a function? In a filter expression?’ Etcetera. Another one: “I have a built a useful filter expression, i.e. a string. How can I use it as a parameter? How can I programmatically adapt it for other uses?”

My 2 cts.

Greetings,
Sjaak

It looks like I can define a multiline string variable like this:

<$let text="line1
line2
line3"

At least this makes the test pass.

But since this was found by trial and error, and I didn’t find a confirmation in the TiddlyWiki docs (yet), I’d like a confirmation that this is the correct way of declaring a multiline string value of a variable.

This is exactly the kind of missing LEGO pieces when it comes to documentation. TiddlyWiki documentation feels more like Linux/BSD man pages - it is good to use as reference, it is good to look up things you know about when you forget the details. It’s sometimes harder to use it to learn new things.

I have an ontopic example:

https://tiddlywiki.com/#Functions

Here’s a quote from that page:

Functions can be invoked in several ways:

  • Directly invoke functions whose names contain a period as custom filter operators with the syntax [my.fun[value]] or [.myfun[value]]

This is an important building block for the TiddlyWiki skill that can be conventionally called Filtercraft.

But while it is making a great promise for basic use cases, I, as a learner, get totally stuck with a “next level” example. I’ll try to elaborate:

For those familiar with reusable pieces of code - called functions in this context - being able to use functions is a game changer. Yet an even more powerful programming tool is the ability to “nest” them, as in do things not only like func1(x), func2(y), but also things like func1(func2(y)). I believe in design patterns slang this is called the Composite Pattern. Yet, in context of the quote above, custom user-defined TiddlyWiki functions have to be used in Filters. And Filters are … well, not very nesting friendly, they are chain-like, they are linear. A proof of this are forum posts about filter syntax that is malfunctioning due to wrong syntax of nested "pesky brackets"™ - forum posts that show up quite often. A further reader of TiddlyWiki documentation will find out about “functional” things like “subfilter” or “map”. Yet even knowing about them doesn’t guarantee that the Average Joe TiddlyWiki user who isn’t a programming superstar (yours truly) will be able to connect the pieces together, this is too abstract to get it right the first time. Yes this is exactly my story - despite knowing all these bits, I can’t put the puzzle together - it’s not obvious at all to me how to write the above func1(func2(y)) example as a TiddlyWiki filter. And what if I make the example even more complex, by introducing additional input data, like func1(func2(y), z)? The mental fog only grows bigger.

The Grok TiddlyWiki edition may be of help here. Grok TiddlyWiki — Build a deep, lasting understanding of TiddlyWiki

The examples in the “Nesting functions” section there show how to use a function inside another function:

\function .radius-to-area() [.square[]multiply<pi>]

But this is just hardcoding the function name inside the filter expression. This is not exactly composition I think. The function body (which is a filter):

[.square[]multiply<pi>]

just repeats what’s stated in the official documentation - that a function containing a dot in name can be used in a filter.

What I mean is the ability to pass a function as a parameter to another function, because func1(func2()) is exactly that.

Perhaps @sobjornstad could comment on this?

This assertion widget is a cool idea! We could definitely use more dev tools in TW.

In general, thinking about filter pipelines as functions, in the sense of functions in other programming languages, is not going to get you very far in TiddlyWiki. You’re correct that there’s no such thing as composition of functions in the sense that you wrap one in the other. Instead, when you need to do this, you first get the result of the inner function, and then pass it to the outer function.

<$let inner={{{ ... my filter }}}>
  {{{ [my.filter.using.value<inner>] }}}
</$let>

I’m not sure if I’m fully understanding you when you say:

What I mean is the ability to pass a function as a parameter to another function, because func1(func2()) is exactly that.

Passing a function as a parameter to another function would be func1(func2). func1(func2()) is passing the result of a function to another function, which is very different. We did the equivalent of passing the result above. Passing a function itself to a function would look more like the following.

There’s no way to pass a function in a way that preserves its type (to the extent that type is even a meaningful concept on TiddlyWiki variables in the first place), like you would in say JavaScript or Lisp. But you could pass the function by name, like this:

\function f(a, b) ...return-of-f...

<$let my.inner.function="f">
  <$let inner={{{ [function<my.inner.function>,[param-a-value],[param-b-value]] }}}>
    {{{ [my.filter.using.value<inner>] }}}
  </$let>
</$let>

(The main limitation here is that you need to ensure that f is in scope in the location where it’s being called; it can’t be turned into a closure and passed as an object like in some languages.)

I think most of the time people either evaluate the function ahead of time (if this doesn’t run into scoping issues) or pass a filter in as a string and evaluate that, but that’s probably more for historical reasons than anything else (since functions are a pretty new feature); I had to think about how to do this pattern above, but I quite like it.

1 Like

I have a question about this part.

So far the examples in TiddlyWiki docs, as well as examples in GrokTiddlyWiki, show that the body of a function is a filter. And I mean the whole body. Which gently pushes towards the conclusion that functions can be treated as human readable labels for filters, just like variables are labels for values. Is it possible to have wikitext code other than filters in a function body? As in:

\function .myfunc()
<$let inner={{{ ... my filter }}}>
  {{{ [my.filter.using.value<inner>] }}}
</$let>
\end

Or does one have to use something else for this kind of wrapping and computation of intermediate values (returned by functions) for further passing them to other functions? A procedure perhaps?

It’s not exactly func1(func2(y),z), but here’s an example of func2(func1(a1,b1),b2):

\function func1(a,b) [<a>add<b>]
\function func2(a,b) [<a>multiply<b>]
\function compose(a1,b1,b2) [function[func1],<a1>,<b1>] :map[function[func2],<currentTiddler>,<b2>]

<<func1 2 3>>
<<func2 5 6>>

<<compose 2 3 6>>

Fred

2 Likes

No. It is not possible to have other code as filters in a function. It would be a procedure, if you do that.

Elaborating on func2(func1(a1,b1),b2), where func1 and func2 function names are now parameters:

\function func1(a,b) [<a>add<b>]
\function func2(a,b) [<a>multiply<b>]
\function compose(f1,a1,b1,f2,b2) [function<f1>,<a1>,<b1>] :map[function<f2>,<currentTiddler>,<b2>]

<<func1 2 3>>
<<func2 5 6>>

<<compose func1 2 3 func2 6>>

Fred

1 Like