Category Archives: Tips and Tricks

Finding a type in the classpath of an Eclipse Java project

Recently, I started to contribute and maintain Pitclipse, an Eclipse plugin for running mutation testing with PIT.

We needed a mechanism to detect whether JUnit 5 is in the classpath of a Java project (so that we could run PIT with its JUnit 5 plugin).

The first implementation of such a mechanism was rather cumbersome since it manually inspected the Java project’s classpath entries. Something like that:

That’s rather unclear, and there are lots of paths to test.

I opened an issue for investigating an alternative implementation, https://github.com/pitest/pitclipse/issues/149, and I was thinking of something simpler, along these lines:

We could try to load some classes of the JUnit 5 engine by constructing a classloader using the classpath of the Java project.
Something like (untested):


That’s simpler and cleaner, but we can do better than that πŸ™‚

JDT provides a lot of API for such things. For what we need to do, we can simply rely on IJavaProject method findType:

IType findType(String fullyQualifiedName)
Returns the first type (excluding secondary types) found following this project’s classpath with the given fully qualified name or null if none is found.

So it’s just a matter of creating a reusable method, e.g.,

And we can simply call it by specifying the fully qualified name of a type that we know belongs to JUnit 5 (PR https://github.com/pitest/pitclipse/pull/199):

Now we don’t even have to test several cases: we simply rely on the correctness of the implementation of findType. This is expected to work on Java projects, Maven projects, Gradle projects, etc.

Cropping images with KDE Gwenview

I’ve always been using GIMP for cropping images (for articles, books, etc.), and it works pretty well, but I never thought that you could do the same much quicker with the default image viewer of KDE, Gwenview. In KDE Plasma, I’ve always used Gwenview as an image viewer, but it’s much more:

Gwenview is a fast and easy to use image viewer by KDE, ideal for browsing and displaying a collection of images.

Features:

  • Supports simple image manipulations: rotate, mirror, flip, and resize
  • Supports basic file management actions such as copy, move, delete, and others

In fact, you just open an image with Gwenview:

Press “Show Editing Tools” and then select “Crop” from the palette:

The box for cropping is ready to be resized:

When you’re done, just press ENTER:

and the image is ready to be saved!

With GIMP, the number of steps is much more significant.

Moreover, with Gwenview, once you cropped and saved the current image, you just press the right arrow to go to the next one. Much faster!

Locate and BTRFS

I’ve always been using the locate command (provided by the mlocate package or by the new plocate package), which quickly searches for files and directories by their names. The command relies on the database built by the command updatedb (which should be run periodically, e.g., by enabling the plocate-updatedb.timer service for plocate).

Unfortunately, by default, it does go well with the BTRFS filesystem and its subvolumes (see, e.g., this bug), resulting in empty results for all searches basically.

Fortunately, the solution is quite simple:

  1. edit the file /etc/updatedb.conf
  2. replace PRUNE_BIND_MOUNTS = “yes” with PRUNE_BIND_MOUNTS = “no”
  3. save, exit and re-run updatedb

Then, you can enjoy locate’s search results πŸ™‚

Xtext 2.27.0: update your Xbase compiler tests

If you update to Xtext 2.27.0 and have compiler tests for your Xbase DSL that assert the output of the compilation, you’ll get lots of failures after the update.

I am guilty of that πŸ˜‰
Well, for a good reason, at least πŸ™‚

In fact, I worked on this issue: https://github.com/eclipse/xtext-extras/issues/772 and its fix is included in Xtext 2.27.0.

Now, the Xbase compilation mechanism does not generate useless empty lines anymore (before, it added lines with two spaces). Your compiler tests will fail because the output is different.

I personally fixed my tests in my DSLs by simply using the Find/Replace mechanism of Eclipse with this substitution pattern (there are two space characters between the tab character and the newline character):

If you have deep nesting in your compilation output, you might have to repeat this substitution with more than two characters, but this should not be required unless you generate nested classes or something like that.

With the above substitution a test like the following one:

will become like the following one (you see the difference: no empty line with two characters between the two generated constructors:

Now your tests should be fixed πŸ™‚

Configure Arch Pacman

Pacman is the package manager in Linux Arch and Linux Arch-based distributions.

I’ve been using EndeavourOS for some time, and I enjoy it. EndeavourOS is pretty close to vanilla Arch. I also experimented with pure Arch (more on that in future blog posts). However, the output of pacman in EndeavourOS is much more excellent and “eye candy” than in Arch. However, it’s just a matter of configuring /etc/pacman.conf a bit in Arch to have the “eye candy” output.

These are the options to enable in the [options] section in that file (the ParallelDownloads does not have to with the output, but it’s a nice optimization):

Without these options, this is the output of pacman (e.g., during an upgrade):

And this is the output with the options above enabled:

Besides the colors, you can spot c’s for the progress representing “Pacman,” the video-game character, eating candies (that’s the aim of the option ILoveCandy)… waka waka waka!Β  πŸ™‚

The colors are also helpful when searching for packages:

Happy Pacman! πŸ™‚

macOS: switch between different windows of the same application

Maybe this is well-known to macOS users, but it wasn’t clear to me as a Linux user.

As a Linux user, I’m used to using Alt+Tab to switch between different windows. But I also use the shortcut to switch between different windows of the same application. In Gnome, the shortcut is Alt+<the key above Tab>, which is cool because it works with any keyboard layout. In KDE it is Alt+backtick (`), which has to be changed in Italian keyboards, like mine to Alt+\. Indeed, in the Italian keyboard layout, the key over tab is \.

In macOS it’s the same as in KDE: the shortcut is bound by default to ⌘+`, which of course it’s unusable in Italian keyboards (you should use a complex combination of keys only to insert the backtick ` character). You then have to configure the shortcut “Move focus to next window”, which is quite counterintuitive to me (I had always thought that it wasn’t possible in macOS to switch between windows of the same application if not by using the touchpad gesture or by pressing the down key after using the standard switcher):

Change it to something suitable for your keyboard layout. For the Italian layout I change it to ⌘+\:

And then you’re good to go! πŸ™‚

Dropbox and Gnome 42

Now that Gnome 42 has been released and available in most Linux distributions, I started experiencing problems with the Dropbox icon in the system tray.

First of all, I have no problem with Ubuntu 22.04, which comes with the extension “AppIndicator and KStatusNotifierItem Support” https://extensions.gnome.org/extension/615/appindicator-support/. Moreover, I think the problem is not there because, while Ubuntu 22.04 ships Gnome 42, it still ships Nautilus in version 41.

In Fedora and EndeavourOS, I usually install the same extension in the Gnome DE, and it has been working quite well.

Unfortunately, with Gnome 42 (provided by Fedora 36 and currently by EndeavourOS), I started experiencing problems, even with the extension above installed and activated.

If you had already installed Dropbox in your Gnome 41 DE and upgraded to Gnome 42 (e.g., you upgraded Fedora 35 to Fedora 36 after installing Dropbox), the icon is clickable. Still, you get a context menu always saying “Connecting…”

At least you can access “Preferences…”.

However, suppose you had never installed Dropbox in that Gnome 42 environment. In that case, the icon in the system tray appears (again, after installing the above extension), but no matter how you click on that, no context menu appears at all. That’s a disgrace because you cannot access Dropbox preferences, like “selective sync” (you have to use the command line, as I suggested in the previous post).

Instead of the extension “AppIndicator and KStatusNotifierItem Support” (disable it if you had already activated that), you can use the extension “Tray Icons: Reloaded,” https://extensions.gnome.org/extension/2890/tray-icons-reloaded/. Install it, activate it, logout and login, and now the context menu works as expected:

Remember that this extension does not seem to support all system tray icons. For example, Variety does not seem to be supported.

At least you can use this extension to set up Dropbox (e.g., selective sync) and then go back to the previous extension!

Problems with Linux 5.13 in LG GRAM 16

I recently bought an LG GRAM 16 and I really enjoy that (I’ll blog about that in the near future, hopefully). I had no problems installing Linux, nor with Manjaro Gnome (Phavo) neither with Kubuntu.

However, in Manjaro Gnome I soon started to note some lags, especially with the touchpad and some repainting issues. I had no problems with Kubuntu (it was 21.04). The main difference was that Manjaro was using Linux kernel 5.13, while Kubuntu 21.04 was using Linux kernel 5.11. As soon as I updated to Kubuntu 21.10, which comes with Linux kernel 5.13, I started to have the same problems also in Kubuntu.

Long story short: switching to Linux kernel 5.14 on both systems solved all the problems πŸ™‚

In Manjaro you can use its kernel management system. Alternatively, from the command line, you can run

On (K)ubuntu things are slightly more complicated because the current version 21.10 does not provide a package for kernel 5.14.

However, you can manually download the DEB files of the kernel (and kernel headers) from the mainline repository https://kernel.ubuntu.com/~kernel-ppa/mainline/. Then, you run dpkg -i on all such downloaded files. However, I prefer to use a nice GUI for such mainline kernels, mainline, https://github.com/bkw777/mainline. It’s just a matter of adding the corresponding PPA repository and installing it:

The GUI application is called “Ubuntu Mainline Kernel Installer”. You select the kernel you want (in this case I’m choosing the latest version of the stable 5.14 version) and choose Install. Reboot and you’re good to go πŸ™‚

Fixing Right Click Touchpad in PineBook Pro

I recently bought a PineBook Pro (maybe I’ll review it in the future in another post). What annoyed me first was that the right click on the touchpad with a two-finger tap was basically unusable: you should be extremely fast.

In some forums, the solution is to issue this command

but then you have to make it somehow permanent.

Actually, it’s much easier than that: just use the KDE Setting (Tap Detection,Β Maximum time), and this will make it permanent right away:

Caching dependencies in GitHub Actions

I recently started to port all my Java projects from Travis CI to GitHub Actions, since Travis CI changed its pricing model. (I’ll soon update also my book on TDD and Build Automation under that respect.)

I’ve always used caching mechanisms during the builds in Travis CI, to speed up the builds: caching Maven dependencies, especially in big projects, can save a lot of time. In my case, I’m mostly talking of Eclipse plug-in projects, built with Maven/Tycho, and the target platform resolution might have to download a few hundreds of megabytes. Thus, I wanted to use caching also in GitHub Actions, and there’s an action for that.

In this post, I’ll show my strategies for using the cache, in particular, using different workflows based on different operating systems, which are triggered only on some specific events. I’ll use a very simple example, but I’m using this strategy currently on this Xtext project: https://github.com/LorenzoBettini/edelta, which uses more than 300 Mb of dependencies.

The post assumes that you’re already familiar with GitHub Actions.

Warning: Please keep in mind that caches will also be evicted automatically (currently, the documentation says that “caches that are not accessed within the last week will also be evicted”). However, we can still benefit from caches if we are working on a project for a few days in a row.

To experiment with building mechanisms, I suggest you use a very simple example. I’m going to use a simple Maven Java project created with the corresponding Maven archetype: a Java class and a trivial JUnit test. The Java code is not important in this context, and we’ll concentrate on the build automation mechanisms.

The final project can be found here:
https://github.com/LorenzoBettini/github-actions-cache-example.

This is the initial POM for this project:

This is the main workflow file (stored in .github/workflows/maven.yml):

This is a pretty standard workflow for a Java project built with Maven. This workflow runs for every push on any branch and every PR.

Note that we specify to cache the directory where Maven stores all the downloaded artifacts, ~/.m2.

For the cache key, we use the OS where our build is running, a constant string “-m2-” and the result of hashing all the POM files (we’ll see how we rely on this hashing later in this post).

Remember that the cache key will be used in future builds to restore the files saved in the cache. When no cache is found with the given key, the action searches for alternate keys if the restore-keys has been specified. As you see, we specified as the restore key something similar to the actual key: the running OS and the constant string “-m2-” but no hashing. This way, if we change our POMs, the hashing will be different, but if a previous cache exists we can still restore that and make use of the cached values. (See the official documentation for further details.) We’ll then have to download only the new dependencies if any. The cache will then be updated at the end of the successful job.

I usually rely on this strategy for the CI of my projects:

  • build every pushes in any branch using a Linux build environment;
  • build PRs in any branch also on a Windows and macOS environment (actually, I wasn’t using Windows with Travis CI since it did not provide Java support on that environment; that’s another advantage of GitHub Actions, which provides Java support also on Windows)

Thus, I have another workflow definition just for PRs (stored in .github/workflows/pr.yml):

Besides the build matrix for OSes, that’s basically the same as the previous workflow. In particular, we use the same strategy for defining the cache key (and restore key). Thus, we have a different cache for each different operating system.

Now, let’s have a look at the documentation of this action:

A workflow can access and restore a cache created in the current branch, the base branch (including base branches of forked repositories), or the default branch. For example, a cache created on the default branch would be accessible from any pull request. Also, if the branch feature-b has the base branch feature-a, a workflow triggered on feature-b would have access to caches created in the default branch (main), feature-a, and feature-b.

Access restrictions provide cache isolation and security by creating a logical boundary between different workflows and branches. For example, a cache created for the branch feature-a (with the base main) would not be accessible to a pull request for the branch feature-b (with the base main).

What does that mean in our scenario? Since the workflow running on Windows and macOS is executed only in PRs, this means that the cache for these two configurations will never be saved for the master branch. In turns, this means that each time we create a new PR, this workflow will have no chance of finding a cache to restore: the branch for the PR is new (so no cache is available for such a branch) and the base branch (typically, “master” or “main”) will have no cache saved for these two OSes. Summarizing, the builds for the PRs for these two configurations will always have to download all the Maven dependencies from scratch. Of course, if we don’t immediately merge the PR and we push other commits on the branch of the PR, the builds for these two OSes will find a saved cache (if the previous builds of the PR succeeded), but, in any case, the first build for each new PR for these two OSes will take more time (actually, much more time in a complex project with lots of dependencies).

Thus, if we want to benefit from caching also on these two OSes, we have to have another workflow on the OSes Windows and macOS that runs when merging a PR, so that the cache will be stored also for the master branch (actually we could use this strategy also when merging any PR with any base branch, not necessarily the main one).

Here’s this additional workflow (stored in .github/workflows/pr-merge.yml):

Note that we intercept the event push (since a merge of a PR is actually a push) but we have an if statement that enables the workflow only when the commit message contains the string “Merge pull request”, which is the default message when merging a PR on GitHub. In this example, we are only interested in PR merged with the master branch and with any branch starting with “experiments”, but you can adjust that as you see fit. Furthermore, since this workflow is only meant for updating the Maven dependency cache, we skip the tests (with -DskipTests) so that we save some time (especially in a complex project with lots of tests).

This way, after the first PR merged, the PR workflows running on Windows and macOS will find a cache (at least as a starting point).

We can also do better than that and avoid running the Maven build if there’s no need to update the cache. Remember that we use the result of hashing all the POM files in our cache key? We mean that if our POMs do not change then basically we don’t expect our dependencies to change (of course if we’re not using SNAPSHOT dependencies). Now, in the documentation, we also read

When key matches an existing cache, it’s called a cache hit, […] When key doesn’t match an existing cache, it’s called a cache miss, and a new cache is created if the job completes successfully.

The idea is to skip the Maven step in the above workflow “Updates Cache on Windows and macOS” if we have a cache hit since we expect no new dependencies are needed to be downloaded (our POMs haven’t changed). This is the interesting part to change:

Note that we need to define an id for the cache to intercept the cache hit or miss and the id must match the id in the if statement.

This way, if we have a cache hit the workflow for updating the cache on Windows and macOS will be really fast since it won’t even run the Maven build for updating the cache.

If we change the POM, e.g., switch to JUnit 4.13.1, push the change, create a PR, and merge it, then, the workflow for updating the cache will actually run the Maven build since we have a cache miss: the key of the cache has changed. Of course, we’ll still benefit from the already cached dependencies (and all the Maven plugins already downloaded) and we’ll update the cache with the new dependencies for JUnit 4.13.1.

Final notes

One might think to intercept the merge of a PR by using on: pull_request: (as we did in the pr.yml workflow). However, “There’s no way to specify that a workflow should be triggered when a pull request is merged”. In the official forum, you can find a solution based on the “closed” PR event and the inspection of the PR “merged” event. So one might think to update the pr.yml workflow accordingly and get rid of the additional pr-merge.yml workflow. However, from my experiments, this solution will not make use of caching, which is the main goal of this post. The symptoms of such a problem are this message when the workflow initially tries to restore a cache:

Warning: Cache service responded with 403

and this message when the cache should be saved:

Unable to reserve cache with key …, another job may be creating this cache.

Another experiment that I tried was to remove the running OS from the cache key, e.g., m2-${{ hashFiles(‘**/pom.xml’) }} instead of ${{ runner.os }}-m2-${{ hashFiles(‘**/pom.xml’) }}, and to use a restore accordingly key, like m2- instead of ${{ runner.os }}-m2-. I was hoping to reuse the same cache across different OS environments. This seems to work for macOS, which seems to be able to reuse the Linux cache. Unfortunately, this does not work for Windows. Thus, I gave up that solution.