<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Konstantin Läufer</title>
    <description>The latest articles on DEV Community by Konstantin Läufer (@klaeufer).</description>
    <link>https://dev.to/klaeufer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F497576%2F62e2d737-efa6-4ab8-b40e-fd221dec9f68.jpeg</url>
      <title>DEV Community: Konstantin Läufer</title>
      <link>https://dev.to/klaeufer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/klaeufer"/>
    <language>en</language>
    <item>
      <title>Unicode string length can mean different things in different languages</title>
      <dc:creator>Konstantin Läufer</dc:creator>
      <pubDate>Sat, 06 Apr 2024 18:22:44 +0000</pubDate>
      <link>https://dev.to/klaeufer/unicode-string-length-can-mean-different-things-in-different-languages-3e6j</link>
      <guid>https://dev.to/klaeufer/unicode-string-length-can-mean-different-things-in-different-languages-3e6j</guid>
      <description>&lt;p&gt;I was working on a text processing example across several different programming languages, including C++, Java, Rust, and Scala, and noticed some discrepancies in the results.&lt;/p&gt;

&lt;p&gt;It turned out that these are due to Unicode string length meaning different things in different languages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In Java, Scala, etc., the &lt;code&gt;length()&lt;/code&gt; method returns the number of abstract, high-level characters (glyphs) from a human reader's point of view.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By contrast, in C++, Go, and Rust, the equivalent functions and methods return a result based on the number of bytes required to store those characters.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;jshell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"résumé"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;==&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="err"&gt;❯&lt;/span&gt; &lt;span class="n"&gt;evcxr&lt;/span&gt;
&lt;span class="n"&gt;Welcome&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;evcxr&lt;/span&gt;&lt;span class="py"&gt;. For&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"résumé"&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"résumé"&lt;/span&gt;&lt;span class="nf"&gt;.chars&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"résumé"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// returns 6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apparently it's a bit more complicated in C++.&lt;/p&gt;

</description>
      <category>unicode</category>
      <category>java</category>
      <category>go</category>
      <category>rust</category>
    </item>
    <item>
      <title>Running the Editcp DMR codeplug editor binary on a Mac</title>
      <dc:creator>Konstantin Läufer</dc:creator>
      <pubDate>Fri, 01 Mar 2024 00:29:53 +0000</pubDate>
      <link>https://dev.to/klaeufer/running-the-editcp-dmr-codeplug-editor-on-a-mac-2gl9</link>
      <guid>https://dev.to/klaeufer/running-the-editcp-dmr-codeplug-editor-on-a-mac-2gl9</guid>
      <description>&lt;p&gt;I've been looking for a painless way to run Dale Farnworth's excellent &lt;a href="https://www.farnsworth.org/dale/codeplug/editcp/" rel="noopener noreferrer"&gt;Editcp DMR codeplug editor&lt;/a&gt; on my Mac, mostly because it's more convenient than additionally pulling out my rarely used Linux laptop.&lt;/p&gt;

&lt;p&gt;I vaguely recall trying a few years ago to build it natively on the Mac and this being very complex and ultimately failing. So today I tried a different approach: running Editcp in one of my existing VMware Ubuntu Linux VMs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In short, I'm delighted to report that this works for me once I installed XQuartz on the Mac and libqt5gui5 on Linux!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MacOS Sonoma&lt;/li&gt;
&lt;li&gt;VMware Fusion&lt;/li&gt;
&lt;li&gt;Ubuntu 22.04 LTS 64-bit VM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Steps for installing and running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;brew install xquartz&lt;/li&gt;
&lt;li&gt;ssh -X ubuntuvm.local&lt;/li&gt;
&lt;li&gt;sudo apt install libqt5gui5&lt;/li&gt;
&lt;li&gt;/opt/bin/editcp&lt;/li&gt;
&lt;li&gt;choose to connect the USB device to Linux&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>amateurradio</category>
      <category>hamradio</category>
      <category>dmr</category>
      <category>macos</category>
    </item>
    <item>
      <title>JaCoCo doesn't directly support vacuously true 100% branch coverage</title>
      <dc:creator>Konstantin Läufer</dc:creator>
      <pubDate>Sun, 28 Jan 2024 00:45:48 +0000</pubDate>
      <link>https://dev.to/klaeufer/jacoco-doesnt-support-vacuously-true-100-code-coverage-1caa</link>
      <guid>https://dev.to/klaeufer/jacoco-doesnt-support-vacuously-true-100-code-coverage-1caa</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I'm an educator and share most of my thoughts just with my students. Once in a while, I have something to share that might help a wider audience and decided to try that here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring code coverage with JaCoCo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Code_coverage" rel="noopener noreferrer"&gt;Code coverage&lt;/a&gt; is a metric that indicates how thoroughly we're testing. &lt;a href="https://www.eclemma.org/jacoco" rel="noopener noreferrer"&gt;JaCoCo&lt;/a&gt; is a mature, actively developed code coverage tool for Java and other JVM-based languages. For &lt;a href="https://www.eclemma.org/jacoco/trunk/doc/counters.html" rel="noopener noreferrer"&gt;each type of coverage&lt;/a&gt;, such as lines, branches, etc., it keeps track of covered and missed items and generates a report with the corresponding coverage percentages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimal example in Java
&lt;/h2&gt;

&lt;p&gt;Here is a minimal SUT (system under test) in Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"hello world"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a JUnit assertion for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello world"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is the resulting coverage report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[info] Test run started (JUnit Jupiter)
[info] Test hw.TestHelloWorld#getMessage() started
[info] Test run finished: 0 failed, 0 ignored, 1 total, 0.136s
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[info]
[info] ------- Jacoco Coverage Report -------
[info]
[info] Lines: 100% (&amp;gt;= required 90.0%) covered, 0 of 2 missed, OK
[info] Instructions: 100% (&amp;gt;= required 80.0%) covered, 0 of 5 missed, OK
[info] Branches: 0% (&amp;lt; required 100.0%) covered, 0 of 0 missed, NOK
[info] Methods: 100% (&amp;gt;= required 100.0%) covered, 0 of 2 missed, OK
[info] Complexity: 100% (&amp;gt;= required 100.0%) covered, 0 of 2 missed, OK
[info] Class: 100% (&amp;gt;= required 100.0%) covered, 0 of 1 missed, OK
[info]
[info] Check /Users/laufer/Work/teaching/cs335/hello-java-sbt/target/scala-2.12/jacoco/report for detailed report
[info]
[error] java.lang.RuntimeException: Required coverage is not met
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;According to JaCoCo, the required coverage threshold is not met because zero branches were covered.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does JaCoCo calculate this?
&lt;/h2&gt;

&lt;p&gt;In JaCoCo's &lt;a href="https://github.com/jacoco/jacoco/blob/master/org.jacoco.core/src/org/jacoco/core/internal/analysis/CounterImpl.java#L181-L195" rel="noopener noreferrer"&gt;&lt;code&gt;CounterImpl&lt;/code&gt;&lt;/a&gt; class, the coverage percentages are calculated as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;getCoveredRatio&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;covered&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missed&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;covered&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, when there is nothing to be covered or missed, the ratio is &lt;code&gt;Double.NaN&lt;/code&gt; ("not a number"), representing the division of 0 by 0 in this case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;If there are no branches to be covered, shouldn't coverage be automatically (vacuously) 100%? From a discrete math perspective, yes:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If there is no work to be done, then the work is already 100% done.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This corresponds to the understanding in mathematical logic that a universally quantified predicate over an empty set (of any element type) is always true (even if the predicate itself is always false):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="n"&gt;scala&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="py"&gt;forall&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;res0&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's easy to implement this behavior by adding a case distinction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;getCoveredRatio&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missed&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;covered&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missed&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;covered&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After working on this for a bit, I finally found the &lt;a href="https://github.com/jacoco/jacoco/issues/305" rel="noopener noreferrer"&gt;pertinent closed issue from 2015&lt;/a&gt;. The closing comment suggests that they are favoring a continuous math perspective based on the ratio calculation shown above, which returns &lt;code&gt;NaN&lt;/code&gt; even if there are no items missed. They closed comments on this issue, and I don't think they'd consider reopening it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Arguably, this is a leaky abstraction that is inconsistent with the definition of code coverage from a discrete math perspective.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is this even relevant?
&lt;/h2&gt;

&lt;p&gt;Doesn't all real-world code include branches, such as conditionals or switches? &lt;/p&gt;

&lt;p&gt;Well, not necessarily. Arguably, the fewer the better from a readability and maintenance point of view. Event-based systems, such as this &lt;a href="https://github.com/lucoodevcourse/stopwatch-android-java" rel="noopener noreferrer"&gt;Android stopwatch&lt;/a&gt;, might not need explicit branching in their control flow when using structural techniques instead, such as attaching listeners to different event sources and applying the &lt;a href="https://en.wikipedia.org/wiki/State_pattern" rel="noopener noreferrer"&gt;State pattern&lt;/a&gt; to implement the underlying state-based behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is obviously a rather minor issue but regularly throws off my students. The path of least resistance seems to be changing the JaCoCo branch coverage threshold in your build configuration: If your SUT happens to have no branches, set the branch coverage threshold to 0, otherwise set it to the desired percentage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="n"&gt;jacocoReportSettings&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nc"&gt;JacocoReportSettings&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;withThresholds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;JacocoThresholds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>java</category>
      <category>testing</category>
      <category>codecoverage</category>
      <category>metrics</category>
    </item>
  </channel>
</rss>
