using TCL expand in if statement - if-statement

I try to use an expand of a list of arguments ($options) in an if-statement in tcl
if {! [runCommandInRepo $componentpath git init {*}$options] } { exit 1 }
but I get an error saying there are "extra characters after close-brace"
How do you expand a list inside an if statement

It sounds like you're using a very old version of Tcl there, probably 8.4 or before. The expansion syntax was added in Tcl 8.5. (8.4 is no longer supported, FYI.)
The “fix” is to use eval carefully:
if {! [eval [list runCommandInRepo $componentpath git init] [lrange $options 0 end]] } { exit 1 }
Yes, that is eval [list …] [lrange … 0 end]. That guarantees that everything is de-fanged of all possible failure modes (or at least that they'll reliably generate a nice error message telling you exactly what the problem is).
But really, upgrade please!

Related

How to integrate tcltest test suite in git pre-commit hook

I'm working on a TCL project, for which version control is based on Git. So, in order to produce good-quality code, I set up put execution of the tests in pre-commit hook.
However, even if they are executed (trace is shown in command-line), and one of the tests is failed, Git performs the commit. So I launched the hook manually to check the error code, and I figured out that it is null, explaining why Git does not stop:
$ .git/hooks/pre-commit
++++ FlattenResult-test PASSED
(...)
==== CheckF69F70 FAILED
==== Content of test case:
(...)
==== CheckF69F70 FAILED
$ echo $?
0
(Launching the tests script with tclsh also results in $? to be 0.)
So my question is about this last line: why is $? equal to 0, when one of the tcl tests is failed? And how can I achieve a simple pre-commit hook that stops on failure?
I read and reread the tcltest documentation, but saw no setting or information about this error code. And I would really like not to have to parse the tcl tests output, to check if ERROR or FAILED is present...
Edit: versions
TCL version : 8.5
tcltest version: 2.3.4
This depends on how you run your test suite. Normally you run a file called tests/all.tcl which may look something like this:
package require Tcl 8.6
package require tcltest 2.5
namespace import tcltest::*
configure -testdir [file dirname [file normalize [info script]]] {*}$argv
runAllTests
That final runAllTests returns a boolean indicating success (0) or failure (1). You can use that to generate an exit code by changing the last line to:
exit [runAllTests]
I use this redefinition in some of my test scripts:
# Exit non-zero if any tests fail.
# tcltest's `cleanupTests` resets the numTests array, so capture it first.
proc cleanupTests {} {
set failed [expr {$::tcltest::numTests(Failed) > 0}]
uplevel 1 ::tcltest::cleanupTests
if {$failed} then {exit 1}
}
After some research, I could make it work, even though several factors were against me:
I have to use an old TCL version (8.5) with tcltest version 2.3.4, in which runAllTests returns nothing;
I forgot to write cleanupTests at the end of test scripts, as the documentation is not really clear about its usage. (It is not clearer now. I just figured out it is needed if you want to get your tests run by runAllTests, which is really not obvious).
And here is my solution, mostly based on Hai's DevBits blog post:
all.tcl
package require tcltest
::tcltest::configure (...)
proc ::tcltest::cleanupTestsHook {} {
variable numTests
set ::exitCode [expr {$numTests(Total) == 0 || $numTests(Failed) > 0}]
}
::tcltest::runAllTests
exit $exitCode
Some thoughts about it:
I added $numTests(Total) == 0 as a failure condition: this means that no tests was found, which is clearly an erroneous condition;
This doesn't catch exceptions in the configuration of the tests, for instance a source command that points to a non-existing file, revealing some failure in tests scaffolding. This would be catched as error in other test framewords (ah, pytest, I miss you!)

Embedded Tcl: Does Tcl autocomplete commands?

We have a Tcl built in our C/C++ application, I found the place in our code where Tcl_EvalObjv is called if the command was not found. I have to admit that the code is pretty old and not many of our developers know what happens in this module.
It looks like this:
// ... there is some checking if command is registered etc., it fails and the code goes here:
std::vector<Tcl_Obj*> tclArgs = { NULL };
for (int i = 1; i < objc; ++i)
tclArgs.push_back(objv[i]);
tclArgs.shrink_to_fit();
// ...
tclArgs[0] = ::Tcl_NewStringObj(ORIGINAL_UNKNOWN, ORIGINAL_UNKNOWN_SIZE);
Tcl_IncrRefCount(tclArgs[0]);
::Tcl_ExposeCommand(pInterp, ORIGINAL_UNKNOWN, ORIGINAL_UNKNOWN);
result = ::Tcl_EvalObjv(pInterp, objc, &tclArgs[0], TCL_EVAL_GLOBAL); //<--
::Tcl_HideCommand(pInterp, ORIGINAL_UNKNOWN, ORIGINAL_UNKNOWN);
// ORIGINAL_UNKNOWN is char* it is just "unknown"
We have handlers for commands in our application, while executing Tcl_EvalObjv in CmdUnknown() function Tcl sometimes calls different commands. Examples below:
List of existing commands: "banana", "applepie", "carpet", "card"
Command: "apple", Tcl calls "applepie" (wrong, "apple" is not "applepie")
Command: "blah", Tcl gives error (correctly).
Command: "car", Tcl gives error (correctly, maybe because of 2 similar commands).
Is there are some kind of mechanism that Tcl does when it fails in searching for command? The thing is that I can't find anything that is related to our code that would complete the commands so maybe Tcl does?
As glenn hinted at, Tcl in interactive (REPL) mode allows for dispatching commands using some minimal but unambiguous name prefix. I cannot tell how your embedded Tcl is configured, initialised, and ended up being run as in interactive mode. However, you may want to try to "turn off" (toggle) the interactive mode, by either:
unset ::tcl_interactive
or
set ::tcl_interactive 0
All of this is implemented by the default unknown handler. Watch out for how the list of cmds is looked up and how it is treated differently when tcl_interactive is true or false:
puts [info body unknown]

how to check in TCL if valid x display has been set

In c++ code we can use the following code to check if valid x display has been set
if (!XOpenDisplay(NULL)) {
}
How to check in TCL if valid x display has been set. What is equivalent code for above code in TCL.
The simplest method is to see if package require Tk fails. Alas, this can fail for other reasons too, but even so.
if {[catch {package require Tk} msg]} {
puts stderr "No DISPLAY or PATH problem: $msg"
exit 1
}

Comparison operator substitution for IF from FOR cycle in batch scripts

I would like to ask your help in a possibly simple situation (where the solution is yet unknown for me).
I am trying to provide a variable for the if command to make a bit more "dynamic" the code, but this fails for me with:
% was unexpected at this time.
Here is a simple example for that:
> for %i in (NEQ) do (if 1 %i 2 echo jo)
%i was unexpected at this time.
While the following works like charm:
>set oper=NEQ
>for %i in (NEQ) do (if 1 %oper% 2 echo works)
works
As I should stay in the for loop (and I get the actual operator from the for loop in the real code), I am really stuck how to solve it...
Tried to play with EnableDelayedExpansion as well, but !variable! instead of the operator is refused as well. Is there a way to submit the variable in a FOR loop for IF, without major modifications in the script?
Is there a way to submit the variable in a FOR loop for IF?
No!
Because, the IF statement has it's own parser and it expects some tokens already expanded at this parse phase.
So it's neither possible to expand the options nor the operator, nor NOT.
But it's allowed to expand the values with delayed or FOR-variables.
setlocal EnableDelayedExpansion
set myOperator=EQU
IF 1 %myOperator% 1 echo Works
IF 1 !myOperator! 1 echo FAILS
for %%O in (EQU) do IF 1 %%O 1 echo Works
The percent expansion works, as it is expanded before the IF parser gets the line.
If you really need to use variable operators, then you need to use a function.
for %%O in (EQU) do call :myFunc %%O
exit /b
:myFunc
IF ONE %1 ONE echo Works
exit /b
EDIT:
And the special parser is also the cause, that it's not possible to use here a CALL expansion for the IF statement.
Like this idea
set myOperator=EQU
CALL IF 1 %%myOperator%% 1 echo Works
This should expand to IF 1 EQU 1 echo Works, but the combination of CALL and IF fails always.

tools for testing vim plugins

I'm looking for some tools for testing vim scripts. Either vim scripts that
do unit/functional testing, or
classes for some other library (eg Python's unittest module) that make it convenient to
run vim with parameters that cause it to do some tests on its environment, and
determine from the output whether or not a given test passed.
I'm aware of a couple of vim scripts that do unit testing, but they're sort of vaguely documented and may or may not actually be useful:
vim-unit:
purports "To provide vim scripts with a simple unit testing framework and tools"
first and only version (v0.1) was released in 2004
documentation doesn't mention whether or not it works reliably, other than to state that it is "fare [sic] from finished".
unit-test.vim:
This one also seems pretty experimental, and may not be particularly reliable.
May have been abandoned or back-shelved: last commit was in 2009-11 (> 6 months ago)
No tagged revisions have been created (ie no releases)
So information from people who are using one of those two existent modules, and/or links to other, more clearly usable, options, are very welcome.
vader.vim is easy, and amazing. It has no external dependencies (doesn't require ruby/rake), it's a pure vimscript plugin. Here's a fully specified test:
Given (description of test):
foo bar baz
Do (move around, insert some text):
2Wiab\<Enter>c
Expect:
foo bar ab
cbaz
If you have the test file open, you can run it like this:
:Vader %
Or you can point to the file path:
:Vader ./test.vader
I've had success using Andrew Radev's Vimrunner in conjunction with RSpec to both test Vim plugins and set them up on a continuous integration server.
In brief, Vimrunner uses Vim's client-server functionality to fire up a Vim server and then send remote commands so that you can inspect (and verify) the outcome. It's a Ruby gem so you'll need at least some familiarity with Ruby but if you put the time in then you get the full power of RSpec in order to write your tests.
For example, a file called spec/runspec.vim_spec.rb:
require "vimrunner"
require "fileutils"
describe "runspec.vim" do
before(:suite) do
VIM = Vimrunner.start_gui_vim
VIM.add_plugin(File.expand_path('../..', __FILE__), 'plugin/runspec.vim')
end
after(:all) do
VIM.kill
end
it "returns the current path if it ends in _test.rb" do
VIM.echo('runspec#SpecPath("foo_test.rb")').should == "foo_test.rb"
VIM.echo('runspec#SpecPath("bar/foo_test.rb")').should == "bar/foo_test.rb"
end
context "with a spec directory" do
before do
FileUtils.mkdir("spec")
end
after do
FileUtils.remove_entry_secure("spec")
end
it "finds a spec with the same name" do
FileUtils.touch("spec/foo_spec.rb")
VIM.echo('runspec#SpecPath("foo.rb")').should == "spec/foo_spec.rb"
end
end
end
I've written about it at length in "Testing Vim Plugins on Travis CI with RSpec and Vimrunner" if you want more detail.
There is another (pure Vimscript) UT plugin that I'm maintaining.
It is documented, it comes with several examples, and it is also used by my other plugins.
It aims at testing function results and buffer contents, and displaying the failures in the quickfix window. Exception callstacks are also decoded. AFAIK, it's the only plugin so far (or at least the first) that's meant to fill the quickfix window. Since then, I've added helper scripts to produce test results with rspec (+Vimrunner)
Since v2.0 (May 2020), the plugin can also test buffer content -- after it has been altered with mappings/snippets/.... Up until then I've been using other plugins. For instance, I used to test my C++ snippets (from lh-cpp) on travis with VimRunner+RSpec.
Regarding the syntax, for instance the following
Assert 1 > 2
Assert 1 > 0
Assert s:foo > s:Bar(g:var + 28) / strlen("foobar")
debug AssertTxt (s:foo > s:Bar(g:var+28)
\, s:foo." isn't bigger than s:Bar(".g:var."+28)")
AssertEquals!('a', 'a')
AssertDiffers('a', 'a')
let dict = {}
AssertIs(dict, dict)
AssertIsNot(dict, dict)
AssertMatch('abc', 'a')
AssertRelation(1, '<', 2)
AssertThrows 0 + [0]
would produce:
tests/lh/README.vim|| SUITE <[lh#UT] Demonstrate assertions in README>
tests/lh/README.vim|27 error| assertion failed: 1 > 2
tests/lh/README.vim|31 error| assertion failed: s:foo > s:Bar(g:var + 28) / strlen("foobar")
tests/lh/README.vim|33 error| assertion failed: -1 isn't bigger than s:Bar(5+28)
tests/lh/README.vim|37 error| assertion failed: 'a' is not different from 'a'
tests/lh/README.vim|40 error| assertion failed: {} is not identical to {}
Or, if we want to test buffer contents
silent! call lh#window#create_window_with('new') " work around possible E36
try
" :SetBufferContent a/file/name.txt
" or
SetBufferContent << trim EOF
1
3
2
EOF
%sort
" AssertBufferMatch a/file/NAME.txt
" or
AssertBufferMatch << trim EOF
1
4
3
EOF
finally
silent bw!
endtry
which results into
tests/lh/README.vim|78 error| assertion failed: Observed buffer does not match Expected reference:
|| ---
|| +++
|| ## -1,3 +1,3 ##
|| 1
|| -4
|| +2
|| 3
(hitting D in the quickfix window will open the produced result alongside the expected result in diff mode in a new tab)
I've used vim-unit before. At the very least it means you don't have to write your own AssertEquals and AssertTrue functions. It also has a nice feature that lets you run the current function, if it begins with "Test", by placing the cursor within the function body and typing :call VUAutoRun().
The documentation is a bit iffy and unfinished, but if you have experience with other XUnit testing libraries it won't be unfamiliar to you.
Neither of the script mentioned have ways to check for vim specific features - you can't change buffers and then check expectations on the result - so you will have to write your vimscript in a testable way. For example, pass strings into functions rather than pulling them out of buffers with getline() inside the function itself, return strings instead of using setline(), that sort of thing.
There is vim-vspec.
Your tests are written in vimscript and you can write them using a BDD-style (describe, it, expect, ...)
runtime! plugin/sandwich/function.vim
describe 'Adding Quotes'
it 'should insert "" in an empty buffer'
put! = ''
call SmartQuotes("'")
Expect getline(1) == "''"
Expect col('.') == 2
end
end
The GitHub has links to a video and an article to get you started:
A tutorial to use vim-vspec by Vimcasts.org [the video]
Introduce unit testing to Vim plugin development with vim-vspec [the article]
For functional testing, there's a tool called vroom. It has some limitations and can take seconds-to-minutes to get through thorough tests for a good size project, but it has a nice literate testing / documentation format with vim syntax highlighting support.
It's used to test the codefmt plugin and a few similar projects. You can check out the vroom/ dir there for examples.
Another few candidates:
VimBot - Similar to VimRunner in that it's written in Ruby and allows you to control a vim instance remotely. Is built to be used with the unit testing framework RSpec.
VimDriver - Same as VimBot except done in Python instead of Ruby (started as a direct port from VimBot) so you can use Python's unit testing framework if you're more familiar with that.