<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://pureooze.com">
	<title>Pureooze - Ramblings</title>
	<subtitle>Rambling about software and making it.</subtitle>
	<link href="https://pureooze.com/blog/feed.xml" rel="self"/>
	<link href="https://pureooze.com/blog"/>
	<updated>2025-10-07T00:00:00Z</updated>
	<id>https://pureooze.com/blog</id>
	<author>
		<name>Uzair Shamim</name></author>
	<entry>
		<title>Memory Bank: Labels In HTML</title>
		<link href="https://pureooze.com/blog/posts/2025-10-07-memory-bank-labels-in-html/"/>
		<updated>2025-10-07T00:00:00Z</updated>
		<id>https://pureooze.com/blog/posts/2025-10-07-memory-bank-labels-in-html/</id>
		<content xml:lang="en" type="html">&lt;p&gt;What is the right way to label something in HTML?&lt;/p&gt;
&lt;p&gt;This came up in something I was working on recently.  I always forget the best practices for labeling so I wanted to write this blog post to help me remember... and as a quick reference when I inevitably forget 😉.&lt;/p&gt;
&lt;h2 id=&quot;default-accessible-names&quot; tabindex=&quot;-1&quot;&gt;Default Accessible Names &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2025-10-07-memory-bank-labels-in-html/#default-accessible-names&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some elements provide a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Accessible_name&quot;&gt;default accessible name&lt;/a&gt; that can be used by screen readers to provide a label for a given element. For example in a button the value between the open and close tags is the default accessible name.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;this is a close button&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sometimes the default accessible name is inaccurate and a separate label needs to be provided to give users accurate context. In these situations labels can help.&lt;/p&gt;
&lt;p&gt;Imagine if the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; in the code above needed to have &lt;code&gt;X&lt;/code&gt; as its text. This is not very informative to screen reader users and we need a better way to indicate what this button does. There are also many elements that do not have default accessible names, so we may need to provide labels for them as well.&lt;/p&gt;
&lt;h2 id=&quot;labeling-elements&quot; tabindex=&quot;-1&quot;&gt;Labeling Elements &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2025-10-07-memory-bank-labels-in-html/#labeling-elements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;title&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/title&quot;&gt;attribute&lt;/a&gt; contains text to provide advisory information related to its target element. This information is often presented to users through a tooltip and read by &lt;strong&gt;some&lt;/strong&gt; screen readers. Note that &lt;code&gt;title&lt;/code&gt; is a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes&quot;&gt;global attribute&lt;/a&gt; so it is available on all HTML elements.&lt;/p&gt;
&lt;p&gt;Unfortunately because &lt;code&gt;title&lt;/code&gt; was introduced in very early versions of HTML there are several accessibility issues with it which created the need for more modern approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-labelledby&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aria-label&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element provides a &lt;strong&gt;visible&lt;/strong&gt; caption for an element. It is often used in user interfaces like forms to provide context on a field. Label requires using the &lt;code&gt;for&lt;/code&gt; attribute to reference another elements via their &lt;code&gt;id&lt;/code&gt; and it only works with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Content_categories#labelable&quot;&gt;labelable elements&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;my-ui&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;blog&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;I like writing&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;checkbox&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;blog&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;blog&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;aria-labelledby&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby&quot;&gt;attribute&lt;/a&gt; references another element on the page to provide an accessible label. Note that &lt;code&gt;aria-labelledby&lt;/code&gt; takes priority over ALL other methods of providing accessible names.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;my-label&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;A label&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; aria-labelledby&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;my-label&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;aria-label&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label&quot;&gt;attribute&lt;/a&gt; contains text to label an element when no other label is present. Since &lt;code&gt;aria-label&lt;/code&gt; is &lt;strong&gt;not visible&lt;/strong&gt; it is often used to provide text that is only available to screen readers. Note that &lt;code&gt;aria-label&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label#associated_roles&quot;&gt;does not work with some elements&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; aria-label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;Close&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; X &lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;so-what-should-you-use&quot; tabindex=&quot;-1&quot;&gt;So What Should You Use? &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2025-10-07-memory-bank-labels-in-html/#so-what-should-you-use&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;title&lt;/code&gt; for labelling causes issues for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/title#accessibility_concerns&quot;&gt;several groups of people&lt;/a&gt; (via MDN):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;People using touch-only devices&lt;/li&gt;
&lt;li&gt;People navigating with keyboards&lt;/li&gt;
&lt;li&gt;People navigating with assistive technology such as screen readers or magnifiers&lt;/li&gt;
&lt;li&gt;People experiencing fine motor control impairment&lt;/li&gt;
&lt;li&gt;People with cognitive concerns&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;If you want to hide content from mobile and tablet users as well as assistive tech users and keyboard only users, use the title attribute.&amp;quot;&lt;/p&gt;
&lt;p&gt;— &lt;a href=&quot;https://www.tpgi.com/using-the-html-title-attribute-updated/&quot;&gt;The Paciello Group blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With that in mind it&#39;s safe to say that &lt;code&gt;title&lt;/code&gt; should not be relied on for labeling elements.
The best practice is to use visible labels over invisible labels like &lt;code&gt;aria-label&lt;/code&gt;. Prefer the use of &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; or &lt;code&gt;aria-labelledby&lt;/code&gt; because they provide visible labels. Use &lt;code&gt;aria-label&lt;/code&gt; if these other options are not available. Remember not to use both visible and invisible labels on the same element as the &lt;code&gt;aria-labelledby&lt;/code&gt; attribute always take precedence.&lt;/p&gt;
</content>
	</entry>
	<entry>
		<title>Navigating Technical Debt</title>
		<link href="https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/"/>
		<updated>2024-03-29T00:00:00Z</updated>
		<id>https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/</id>
		<content xml:lang="en" type="html">&lt;p&gt;We don&#39;t spend enough time doing maintenance! We have too much legacy code! We only build new features and never cleanup the old things we made! We have too much technical debt!
Sound familiar? If you have been involved in any meaningful software development project – especially one that is large – you will have heard complaints like this.
It&#39;s a topic that many software developers (including yours truly) are passionate about.&lt;/p&gt;
&lt;p&gt;I would be willing to bet that if you surveyed random developers at a conference, you would find that the majority of them consider technical debt to be a significant problem in their organization.
I would also be willing to bet that they could not agree on &lt;strong&gt;what the problem is&lt;/strong&gt; and &lt;strong&gt;how to address it&lt;/strong&gt;.
The issue is further complicated by the fact that many businesses seem to not care about tech debt.&lt;/p&gt;
&lt;p&gt;In this post I want to explore the idea of technical debt. What is it? Can we measure it? What can we do about it?&lt;/p&gt;
&lt;h2 id=&quot;what-is-technical-debt&quot; tabindex=&quot;-1&quot;&gt;What Is Technical Debt? &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#what-is-technical-debt&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The term is used to describe things from &amp;quot;we don&#39;t use the shiny new framework&amp;quot; to &amp;quot;this code is not written in the style I like&amp;quot; and even &amp;quot;I inherited the code from someone else&amp;quot;.
Let&#39;s start by looking at Ward Cunningham&#39;s original definition:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Shipping first-time code is like going into debt. A little debt speeds development so long as it is paid back promptly with refactoring.
The danger occurs when the debt is not repaid.
Every minute spent on code that is not quite right for the programming task of the moment counts as interest on that debt.
Entire engineering organizations can be brought to a stand-still under the debt load of an unfactored implementation, object-oriented or otherwise.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;- Ward Cunningham&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This definition focuses on technical debt being a trade-off &lt;strong&gt;when developing new features&lt;/strong&gt; and that &lt;strong&gt;it is not always bad&lt;/strong&gt; as long as its addressed promptly.
The problem though is that unlike financial debt, who is responsible for making sure an organization can handle more technical debt?
Can an organization be &amp;quot;technical debt&amp;quot; bankrupt?
When organizations start thinking of this in terms of &amp;quot;debt&amp;quot; they quickly realize that there don&#39;t &lt;em&gt;seem&lt;/em&gt; to be any consequences of the debt, so they ignore it.&lt;/p&gt;
&lt;p&gt;Steve Freeman (whose work I first encountered in the wonderful book &lt;a href=&quot;https://github.com/sf105/goos-code&quot;&gt;Growing Object-Oriented Software, Guided by Tests&lt;/a&gt;) describes technical debt as an &lt;a href=&quot;https://higherorderlogic.com/programming/2023/10/06/bad-code-isnt-technical-debt-its-an-unhedged-call-option.html&quot;&gt;&amp;quot;Unhedged Call Option&amp;quot;&lt;/a&gt;.
This definition highlights the &lt;strong&gt;unpredictability of cost&lt;/strong&gt;.
Unlike debt – where we know what the interest rate will be – options could be infinitely more costly than doing the work in the first place.
I like this definition because it brings forth this idea that the cost can become due suddenly and without warning, and we need to understand where to focus our efforts because the &amp;quot;debt&amp;quot; is a disaster waiting to happen.&lt;/p&gt;
&lt;h2 id=&quot;lensing-to-understand-technical-debt&quot; tabindex=&quot;-1&quot;&gt;Lensing To Understand Technical Debt &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#lensing-to-understand-technical-debt&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/ovG_wltTS7-1920.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/ovG_wltTS7-1920.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;The Tarantula Nebula captured from The Hubble Space Telescope. – Photo by &lt;a href=&quot;https://unsplash.com/@hubblespacetelescope&quot;&gt;NASA Hubble Space Telescope&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Michael Feathers has a great post – &lt;a href=&quot;https://michaelfeathers.substack.com/p/lensing-to-understand&quot;&gt;Lensing to Understand&lt;/a&gt; – about how we change our focus to understand a system.
We focus on the high level system to identify potentially interesting areas and then drill down into details to investigate them.
Once we have a better understanding, we zoom back out to see how it affects the system as a whole.
The idea of &amp;quot;lensing&amp;quot; is really important when it comes to understanding technical debt.&lt;/p&gt;
&lt;p&gt;Starting with a systems view and then drilling down into the details may be a better way to understand the system.
Where do people encounter the most issues?
Are some parts of the system riskier than others?
What parts of the system are most likely to change in the future?
These are the kinds of questions we need to be asking.
Too often we focus on bits of code and forget the context of the systems it&#39;s used in – which is extremely important.&lt;/p&gt;
&lt;p&gt;So how do we identify risks in our codebase?
Adam Tornhill has a great talk on &lt;a href=&quot;https://www.youtube.com/watch?v=w9YhmMPLQ4U&quot;&gt;&amp;quot;Prioritizing Technical Debt&amp;quot;&lt;/a&gt; where he talks about how we can use metrics to identify risks in our codebase.
If we have a way to find out &lt;strong&gt;where to focus our efforts&lt;/strong&gt; we can get a lot more value out of the work we do – &lt;strong&gt;allowing us to ship quicker and build high quality features&lt;/strong&gt;.
I really liked this talk and I recommend watching it.&lt;/p&gt;
&lt;p&gt;Adam has a few metrics that he uses to identify risks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File Hotspots&lt;/strong&gt;: Files that are changed frequently&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Method Hotspots&lt;/strong&gt;: Methods in hotspot files that are changed frequently&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: Files that are hard for humans to understand&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we find places that meet all three of these criteria we have a high risk area that we should focus on.&lt;/p&gt;
&lt;h2 id=&quot;hotspot-analysis&quot; tabindex=&quot;-1&quot;&gt;Hotspot Analysis &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#hotspot-analysis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To demonstrate application of these metrics I am going to run them on the &lt;a href=&quot;https://github.com/pureooze/TwitchEverywhere&quot;&gt;TwitchEverywhere&lt;/a&gt; code – which is a C# library I wrote for a side project.&lt;/p&gt;
&lt;h3 id=&quot;file-hotspots&quot; tabindex=&quot;-1&quot;&gt;File Hotspots &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#file-hotspots&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/1v2jZ9_ZlY-1920.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/1v2jZ9_ZlY-1920.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Mountains in the distance – Photo by &lt;a href=&quot;https://unsplash.com/@dannymc&quot;&gt;Danny Mc&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Let&#39;s start with file hotspots.
I&#39;m going to look at commits from the past year and see which files have been changed the most.
Turns out there is a simple bash command that can do this:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; --since=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;1 year ago&#39;&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; --name-only&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; --pretty=format:&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; sort&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; uniq&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; sort&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; -nr&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running this on the &lt;a href=&quot;https://github.com/pureooze/TwitchEverywhere&quot;&gt;TwitchEverywhere&lt;/a&gt; code gives the following results:&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Data results (top 20, commit count per file for past year)&lt;/summary&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhereCLI/TwitchConnection.cs&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere/Implementation/TwitchConnector.cs&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.Benchmark/MsgBenchmark.cs&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere/Implementation/MessagePlugins/MessagePluginUtils.cs&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere/TwitchEverywhere.cs&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.UnitTests/TwitchConnectorTests/NoticeTests.cs&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.UnitTests/TwitchConnectorTests/PrivMsgTests.cs&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere/Implementation/MessageProcessor.cs&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhereCLI/Program.cs&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.Rest/Implementation/RestApiService.cs&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.Rest/RestClient.cs&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere/ITwitchConnector.cs&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.Irc/Implementation/TwitchConnector.cs&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.Rest/IRestApiService.cs&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.UnitTests/TwitchConnectorTests/ClearChatTests.cs&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.Irc/Implementation/MessageProcessor.cs&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere/Implementation/MessagePlugins/ClearChatPlugin.cs&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere/Implementation/MessagePlugins/PrivMsgPlugin.cs&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere/Types/PrivMsg.cs&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwitchEverywhere.Irc/ITwitchConnector.cs&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/details&gt;
&lt;p&gt;Data is nice, but it&#39;s hard to get a sense of the scale we are dealing with.
Let&#39;s use a small python script to create a visualization of these file commit counts sorted from highest to lowest:&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Python script for plotting the csv to a horizontal bar chart&lt;/summary&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; pandas &lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; pd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; matplotlib&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;pyplot &lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; plt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; Load the CSV file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;df &lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; pd&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;read_csv&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;myFile.csv&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;df_filtered &lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; df&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;df&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;Key&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;].&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;endswith&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;.cs&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;df_sorted &lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; df_filtered&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;sort_values&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt;by&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;Value&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; ascending&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;False&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; Plotting&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;figure&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt;figsize&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;25&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;),&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; dpi&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;bar&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;df_sorted&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;Key&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;],&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; df_sorted&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;Value&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;],&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; color&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;#26196f&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;xlabel&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;File&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;ylabel&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;Number Of Commits&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;Most Modified File By Commit Count&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;xticks&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt;ticks&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;xticks&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;()[&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;],&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; labels&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;xticks&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;()[&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]))&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; hide labels on x-axis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;plt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;show&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/5h5Tkco72h-1250.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/5h5Tkco72h-1250.webp&quot; alt=&quot;Visualization of commit count per file for TwitchEverywhere as a bar chart&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Visualized like this there is a shocking revelation: a small percentage of files are responsible for the vast majority of commits!
What if I told you that the distribution of commits per file in a codebase is common across most codebases?
So common that it&#39;s a pattern that is seen regardless of factors like language, age, or size.
Sounds crazy right? But we can test if it is true.
I ran the file hotspot analysis on the following codebases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/aspnetcore&quot;&gt;ASP.NET Core (.cs files)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/roslyn&quot;&gt;Roslyn (.cs files)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/django/django&quot;&gt;Django (.py files)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/torvalds/linux&quot;&gt;Linux (.c files)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details&gt;
&lt;summary&gt;Results for ASP.NET Core, Roslyn, Django and Linux&lt;/summary&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/FKwg5Z2Kc5-4994.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/FKwg5Z2Kc5-4994.webp&quot; alt=&quot;Results for ASP.NET Core, Roslyn, Django and Linux showing very similar distributions&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;If you are familiar with the &lt;a href=&quot;https://en.wikipedia.org/wiki/Pareto_principle&quot;&gt;Pareto Principle&lt;/a&gt; – these distributions certainly seems to resemble it.
It seems regardless of language, author, age and purpose – the same pattern emerges.
&lt;strong&gt;Why do you think this is the case?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;People do things because they are incentivized to do them.
Adding to existing places is easier because we don&#39;t have to think about context, so it&#39;s faster to do.
The social system incentivizes us to do things fast, but it comes at the cost of other things.
There is a constant pressure to deliver features, and so the path of least resistance is very tempting.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id=&quot;method-hotspots&quot; tabindex=&quot;-1&quot;&gt;Method Hotspots &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#method-hotspots&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/MSDt1ZEDZI-1920.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/MSDt1ZEDZI-1920.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Insect wings through a microscope lens – Photo by &lt;a href=&quot;https://unsplash.com/@ashley_hayes&quot;&gt;Ash Hayes&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;From the previous example we saw that &lt;code&gt;TwitchConnection.cs&lt;/code&gt; was by far the most commited to file in &lt;code&gt;TwitchEverywhere&lt;/code&gt;.
We can use the following algorithm to find its hotspot methods:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the commits for the file in the given time range&lt;/li&gt;
&lt;li&gt;Get the changes for the file in each commit&lt;/li&gt;
&lt;li&gt;Compare the current commit and the next commit&lt;/li&gt;
&lt;li&gt;Count the number of times each method was changed between commits&lt;/li&gt;
&lt;li&gt;Sort the methods by the number of changes&lt;/li&gt;
&lt;li&gt;Visualize the data&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finding method changes between commits (steps 3 and 4) using git is a little tricky – since it doesn&#39;t track methods.
In C# we can use &lt;code&gt;Roslyn&lt;/code&gt; to get the changes for us and I created a small .NET project called &lt;a href=&quot;https://github.com/pureooze/DebtCollector.NET&quot;&gt;DebtCollector.NET&lt;/a&gt; that can be used for this.
It&#39;s a simple library that uses &lt;code&gt;LibGit2Sharp&lt;/code&gt; to extract data out of git and &lt;code&gt;Roslyn&lt;/code&gt; to process the code.&lt;/p&gt;
&lt;p&gt;These are the results for the &lt;code&gt;TwitchConnection.cs&lt;/code&gt; file:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/3puYykuaeP-1250.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/3puYykuaeP-1250.webp&quot; alt=&quot;Visualization of commit count per method in the most committed file as a bar chart&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Data table for xray results&lt;/summary&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method Name&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MessageCallback&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ConnectToRestClient&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PrivMessageCallback&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClearChatCallback&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connect&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ConnectToIrcClient&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NoticeMsgCallback&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClearMsgCallback&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WriteToStore&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaveBufferToFile&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ConnectToIrcClientRx&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WriteMessagesToStore&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClearMessageCallback&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaveBinaryDataToFile&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/details&gt;
&lt;p&gt;With this data its obvious I modify &lt;code&gt;MessageCallback&lt;/code&gt; extremely often.
So this could be an interesting place to focus on during a refactoring session.&lt;/p&gt;
&lt;h3 id=&quot;code-complexity&quot; tabindex=&quot;-1&quot;&gt;Code Complexity &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#code-complexity&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/JhWgzeRXsD-679.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/JhWgzeRXsD-679.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Code complexity of the MessageCallback method with a score of 18 (mildly complex)&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Doing a cyclomatic complexity analysis on the &lt;code&gt;MessageCallback&lt;/code&gt; method in &lt;a href=&quot;https://github.com/pureooze/TwitchEverywhere&quot;&gt;TwitchEverywhere&lt;/a&gt; gives a score of 18 (mildly complex).
It&#39;s not too complex, but I know from experience that this method is modified a lot because I was lazy and didn&#39;t make separate methods for each message type.
The method signature even gives a hint to this.
Instead, all the logic is in one method and ends up being changed really often.
So that&#39;s something I can focus on when I refactor this file.&lt;/p&gt;
&lt;h2 id=&quot;no-silver-bullets&quot; tabindex=&quot;-1&quot;&gt;No Silver Bullets &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#no-silver-bullets&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;There are no silver bullets in software development.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Commit hotspots could be the result of a developer who likes to make a lot of commits.
Without context, it&#39;s hard to know if this is a problem in the specific situation.
Technical debt is a complex problem that can be hard to define and even harder to solve.
We can use metrics like hotspots to identify, investigate and communicate these risks effectively to others.&lt;/p&gt;
&lt;p&gt;I hope this post has given you some ideas on how to navigate technical debt in your organization.
If you have any questions or comments feel free to reach out to me on &lt;a href=&quot;https://toot.community/@pureooze&quot;&gt;Mastodon&lt;/a&gt; or &lt;a href=&quot;https://github.com/pureooze&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/ITEE5wuw9T-697.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/ITEE5wuw9T-697.webp&quot; alt=&quot;Tackling technical debt&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2 id=&quot;additional-notes&quot; tabindex=&quot;-1&quot;&gt;Additional Notes &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#additional-notes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.thekua.com/atwork/2011/05/notes-from-michael-feathers-brutal-refactoring/&quot;&gt;Notes from Michael Feathers’ Brutal Refactoring&lt;/a&gt; &lt;a href=&quot;https://pureooze.com/blog/posts/2024-03-29-navigating-technical-debt/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
	</entry>
	<entry>
		<title>Integration Testing Browser Extensions with Jest</title>
		<link href="https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/"/>
		<updated>2018-08-16T00:00:00Z</updated>
		<id>https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/</id>
		<content xml:lang="en" type="html">&lt;p&gt;Previously I wrote about how I became the maintainer of Saka, an open source browser extension that allows users to search through and load open tabs, browsing history and bookmarks. I talked about how I came up with a solution for unit testing the extension to give me confidence with code changes. I also mentioned that there were issues with integration testing that I ran into which made it difficult to test components that relied on browser APIs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This post was originally posted on August 6th 2017. Things may have changed since then, and this post may no longer be accurate. Proceed with caution.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Today I am happy to report that I have found a way to perform integration testing on extensions and want to share it with you in this post. But before we go down that particular rabbit hole lets first discuss integration testing and why it is useful for validating software.&lt;/p&gt;
&lt;h2 id=&quot;the-testing-trophy&quot; tabindex=&quot;-1&quot;&gt;The Testing Trophy &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/#the-testing-trophy&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/NnyyYcnZ3s-450.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/NnyyYcnZ3s-450.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;https://twitter.com/kentcdodds/status/960723172591992832&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Kent C. Dodds has written about something he calls the ‘Testing Trophy’. If you have heard of the testing pyramid before this is a similar concept — it’s a visualization of how you should prioritize the different types of testing in applications. The title of Kent’s post says it all:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write tests. Not too many. Mostly integration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why does he say this? Kent notes the problem with unit tests is that they only prove individual units work as expected— they do not prove that the units can work together as a whole. Integration testing on the other hand proves that all the components in our project can actually work together as expected.&lt;/p&gt;
&lt;h2 id=&quot;the-need-for-integration-testing&quot; tabindex=&quot;-1&quot;&gt;The Need For Integration Testing &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/#the-need-for-integration-testing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s leave the world of software and look at a real world example. Suppose we wanted to build a sink for a bathroom. There are 4 components to this sink: the faucet, the basin, the drainage system and the water line. Since the drain and water line come with the building we only need to worry about adding the faucet and the basin.&lt;/p&gt;
&lt;p&gt;We go to the store and pick a faucet and basin that we like. We bring them on site and assemble each individually. We confirm that the faucet and basin each work as expected and that they have no defects. Finally we assemble the full sink — hooking up the faucet to the water line and the basin to the drainage. After all our labor we are excited to see our sink in action so we turn on the faucet and what happens? Well…&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/hM2RqCfmIM-500.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/hM2RqCfmIM-500.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;https://natooktesting.wordpress.com/2017/08/24/x-unit-tests-0-integration-tests/&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Oops! While we did check to see that the faucet and basin work on their own we forgot to check if the two were actually compatible. This is why integration testing is valuable — it proves that different components, modules and libraries work together as expected.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; tabindex=&quot;-1&quot;&gt;Solution &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/#solution&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since writing my last post I have managed to get Jest working with Preact, the framework used to create Saka. Jest is a modern testing framework that can run in Node or JSDOM. I will also be using the dom-testing-library to perform the rendering and assertions on my components.&lt;/p&gt;
&lt;p&gt;Just keep in mind that while my specific solutions will be tailored for Preact, they will still work for other frameworks — especially React — with slight modifications for framework specific libraries.&lt;/p&gt;
&lt;p&gt;There is an example Preact extension with Jest setup for reference here: https://github.com/pureooze/extension-testing-example&lt;/p&gt;
&lt;h3 id=&quot;installation&quot; tabindex=&quot;-1&quot;&gt;Installation &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/#installation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;You don’t need to install the preact packages if you use a different framework&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;First you need to install the required dependencies:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;yarn&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; --dev&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; babel-jest&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; babel-plugin-transform-class-properties&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; babel-plugin-transform-react-jsx&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; babel-preset-env&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; babel-preset-react&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; jest&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sinon-chrome&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are using Preact you need to also run the following:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;yarn&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; --dev&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; preact-compat&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; preact-render-to-string&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; preact-test-utils&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; preact-testing-library&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that just like in the previous post we will be using sinon-chrome to mock all browser APIs.&lt;/p&gt;
&lt;h3 id=&quot;configuration-jest-for-preact-only-not-required-for-react&quot; tabindex=&quot;-1&quot;&gt;Configuration Jest (for Preact only, not required for React) &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/#configuration-jest-for-preact-only-not-required-for-react&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With Jest installed you now need to create a config to tell jest how to deal with parsing Preact. If you use another framework like React you don’t need to do this. Create a jest.config.js file in the root directory of your project with the following contents:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  moduleNameMapper&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    &quot;^react-dom/server$&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;      &quot;&amp;#x3C;rootDir&gt;/node_modules/preact-render-to-string/dist/index.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    &quot;^react-addons-test-utils$&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;      &quot;&amp;#x3C;rootDir&gt;/node_modules/preact-test-utils/lib/index.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    &quot;^react$&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;&amp;#x3C;rootDir&gt;/node_modules/preact-compat/lib/index.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    &quot;^react-dom$&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;&amp;#x3C;rootDir&gt;/node_modules/preact-compat/lib/index.js&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  moduleFileExtensions&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;js&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;jsx&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  transform&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    &quot;^.+&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;&#92;&#92;&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;.jsx?$&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;&amp;#x3C;rootDir&gt;/jest.transform.js&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the transform property is telling Jest to apply a custom transformer on all JSX files. To make it work we also need to create a jest.transform.js file:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; babelOptions&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  presets&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; [[&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;env&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; targets&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; node&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;8&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  plugins&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;    [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;      &quot;transform-react-jsx&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;        pragma&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;h&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;babel-jest&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;createTransformer&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;babelOptions&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;create-the-commands&quot; tabindex=&quot;-1&quot;&gt;Create The Commands &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/#create-the-commands&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add the following npm scripts to your package.json so that jest can be run from the command line:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;scripts&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;jest&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;test:watch&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;jest --watch&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;create-the-first-test&quot; tabindex=&quot;-1&quot;&gt;Create The First Test &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/#create-the-first-test&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Jest is smart enough to scan your project and run any files it finds with the .test.js extension. Create a new file called Main.test.js in the tests directory of your project with the following contents where the import Main is the component you want to test:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import &lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; h&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; from &lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import &lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; render&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; cleanup&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; from &lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;preact-testing-library&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import &lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;Main&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; from &lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;../src/example/index&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import &lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;chrome&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; from &lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;sinon-chrome&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;window&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;chrome&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; chrome&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;should render Main component&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#EA9A97;font-style:italic&quot;&gt; getUrl&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; chrome&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;runtime&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getURL&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;popup-content.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;  chrome&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;runtime&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;getURL&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;http://localhost:1234/index.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; container&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; render&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;Main&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; getUrl&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;getUrl&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt; /&gt;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  expect&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;container&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;toMatchSnapshot&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the file is created, open a terminal in the root of your project and run the command:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;yarn&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; test:watch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see jest output something similar to this (assuming the test you created passed):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/vnf8jHkqpX-412.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/vnf8jHkqpX-412.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Congrats! Jest has successfully run and created a snapshot for the test. Jest has created a &lt;strong&gt;snapshots&lt;/strong&gt; directory where all the snapshots are stored. If you open one of them you will see something like this:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; Jest Snapshot v1, https://goo.gl/fbAQLP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;`should render Main component 1`&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&amp;#x3C;div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;  &amp;#x3C;div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    &amp;#x3C;button&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;      id=&quot;changeColor&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    &amp;#x3C;b&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;      http://localhost:1234/index.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    &amp;#x3C;/b&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;    ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;  &amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next time the test runs, Jest will compare the existing snapshot to the snapshot it takes at runtime and notify you if a difference is detected.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Integration testing is valuable when we want to assert the compatibility of the components we create. As Kent C. Dodds writes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write tests. Not too many. Mostly integration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can use Jest to achieve this for modern Javascript projects and browser extensions are no different. With the help of sinon-chrome you can mock out any extension APIs that are used by your components.&lt;/p&gt;
</content>
	</entry>
	<entry>
		<title>Unit Testing Browser Extensions</title>
		<link href="https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/"/>
		<updated>2018-05-21T00:00:00Z</updated>
		<id>https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/</id>
		<content xml:lang="en" type="html">&lt;p&gt;In April I became the maintainer of Saka, a browser extension that allows users to search through their tabs, bookmarks and history. The original goal of Saka was to provide an elegant tab search but this soon evolved to include recently closed tabs, bookmarks and history when the original maintainer eejdoowad recognized that users search for tabs the same way they search bookmarks and history. This was an important insight and it has helped make Saka a valuable productivity tool.&lt;/p&gt;
&lt;p&gt;When I became the maintainer I was surprised at the absence of tests in the project. There were several components with complicated logic but no tests to be found anywhere. One of the most important things I have learned as a developer is that tests are the easiest ways to write reliable, easy to refactor code. Was the old maintainer just lazy? Did he simply not care about the quality of his code? No. The opposite in fact, he cared a lot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This post was originally posted on August 6th 2017. Things may have changed since then, and this post may no longer be accurate. Proceed with caution.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/4lh86SIbr7-640.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/4lh86SIbr7-640.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Saka, a browser extension for searching tabs, recently closed, bookmarks and history.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The issue is that the lack of documentation on the topic means that almost no one is able to test their extension. Having no confidence in my ability to make changes without breaking the code, this was a big problem. But as fate would have it after trying a dozen different approaches I ended up finding a solution.&lt;/p&gt;
&lt;h2 id=&quot;why-we-test&quot; tabindex=&quot;-1&quot;&gt;Why We Test &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#why-we-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As developers we want to be sure that the code we write today is not going to become a burden to maintain later in the lifetime of the application. One way we avoid creating these burdens is by writing tests. The great thing about tests is that outside of just verifying the behavior of functions, tests allow us to provide documentation for future developers. For example by creating unit tests we declare the valid inputs and outputs for a given function. This makes it easier to refactor code because we can have confidence that our code is working correctly when all our tests pass.&lt;/p&gt;
&lt;h3 id=&quot;the-testing-approach&quot; tabindex=&quot;-1&quot;&gt;The Testing Approach &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#the-testing-approach&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This post will focus on setting up the environment and writing some basic unit tests. I have a separate post at &lt;a href=&quot;https://pureooze.com/blog/posts/2018-08-16-integration-testing-browser-extensions-with-jest&quot;&gt;this link&lt;/a&gt; which you can use to perform integration testing.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; tabindex=&quot;-1&quot;&gt;Solution &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#solution&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In my search for a solution to testing Saka, I went through several different testing libraries like Jest, Mocha and Jasmine. One of the biggest challenges for me was that Saka is written using Preact, which causes compatibility issues with other libraries. But after following several examples online, I was finally able to put together a solution using Karma and Jasmine.&lt;/p&gt;
&lt;h3 id=&quot;pre-requisites&quot; tabindex=&quot;-1&quot;&gt;Pre-requisites &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#pre-requisites&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In order to use this solution, your project should use Webpack. The example uses version 4 but this may still work with older versions. While I have not tried, it should be possible to make this work with Gulp after some configuration to make sure everything is bundled properly. You can find a sample webpack config &lt;a href=&quot;https://github.com/pureooze/extension-testing-example/blob/37e5a82cc436fc8256110600255145ad347340f1/webpack.config.js&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/3mdxLhS6kN-703.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/3mdxLhS6kN-703.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3 id=&quot;karma-jasmine&quot; tabindex=&quot;-1&quot;&gt;Karma + Jasmine &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#karma-jasmine&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you are not already familiar with it, Karma is a tool that allows executing JavaScript code in a browser for testing purposes. While it can execute code, it is not capable of testing the code and instead relies on third party libraries like Jasmine and Mocha. When developing Saka I chose Jasmine because I had previous experience using it in other projects.&lt;/p&gt;
&lt;p&gt;The first step to getting Karma and Jasmine setup is to install them:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;yarn&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; jasmine&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; karma&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; karma-chrome-launcher&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; karma-jasmine&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; karma-spec-reporter&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; karma-webpack&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; babel-loader&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; --dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before Karma can start running tests it needs to know what configuration parameters to use. In order to provide these, create a karma.conf.js file in the root of the project. I have provided a sample config here. Note that Karma is capable of running Jasmine on its own, it just needs to be told to use it via the frameworks configuration property.&lt;/p&gt;
&lt;h3 id=&quot;chrome&quot; tabindex=&quot;-1&quot;&gt;Chrome &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#chrome&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Those of you who actually read the karma config may notice that it specifies Chrome as a requirement:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;browsers:&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;ChromeHeadless&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I mentioned earlier, Karma requires an actual browser to run the JavaScript code. This line tells Karma that it should look for Chrome on the system it is running on and launch it in headless mode. The benefits of using headless mode are that you can use the system when the tests are running, instead of being interrupted every 2 seconds when a new test starts to run. Seemed like an obvious win to me.&lt;/p&gt;
&lt;h3 id=&quot;adding-a-test&quot; tabindex=&quot;-1&quot;&gt;Adding A Test &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#adding-a-test&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To start adding tests, create a JavaScript module using the code in &lt;a href=&quot;https://github.com/pureooze/extension-testing-example/blob/4f047127a0735c5d97f3b4c6f2b46b063de61d55/src/simpleModule.js&quot;&gt;this example&lt;/a&gt; under the src directory of your project.
As the name suggests the sum function will simply add up all values passed to it and return the sum.&lt;/p&gt;
&lt;p&gt;Create a test directory in the root of your project — this is where all the tests will live.
Take a look at the karma config file and note &lt;a href=&quot;https://github.com/pureooze/extension-testing-example/blob/4f047127a0735c5d97f3b4c6f2b46b063de61d55/karma.conf.js#L4&quot;&gt;this line&lt;/a&gt;.
It tells karma that to load the tests it must use the test/index.test.js file as the entry point.
In the index.test.js file add &lt;a href=&quot;https://github.com/pureooze/extension-testing-example/blob/4f047127a0735c5d97f3b4c6f2b46b063de61d55/test/index.test.js&quot;&gt;the following code&lt;/a&gt; to import all files inside the test directory ending in .test.js.&lt;/p&gt;
&lt;p&gt;With the config out of the way, add a new file simpleModule.test.js in the test directory with &lt;a href=&quot;https://github.com/pureooze/extension-testing-example/blob/4f047127a0735c5d97f3b4c6f2b46b063de61d55/test/simpleModule.test.js&quot;&gt;the following code&lt;/a&gt;.
This file will house the tests for all the functions in the simpleModule.js file.
The describe blocks are used to categorize the tests in the Jasmine logs so that it is easier to tell which modules have failures.
Individual tests are located within the it() function which needs a description as the first argument and the test function as the second argument.
To learn more about how to write tests using Jasmine you can consult &lt;a href=&quot;https://jasmine.github.io/api/3.0/global&quot;&gt;the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;running-tests&quot; tabindex=&quot;-1&quot;&gt;Running Tests &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#running-tests&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In order to run tests the karma executable can be called directly with the path to the config file passed in as an argument. While this works, a more elegant solution is to add the command to the npm scripts in the package.json file like this. You should now be able to just run yarn test and see the output from Karma like below.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/lQVNzeH0_h-899.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/lQVNzeH0_h-899.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3 id=&quot;testing-with-webextension-apis&quot; tabindex=&quot;-1&quot;&gt;Testing With WebExtension APIs &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#testing-with-webextension-apis&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The problem that developers run into when attempting to test extensions is having to deal with the WebExtension APIs in tests. The problem is that the environment the tests run in — that is as a webpage in chrome — does not have access to the APIs. This becomes an issue as Jasmine will throw an error because anything with browser.* will be undefined.&lt;/p&gt;
&lt;p&gt;To overcome this issue you need to install &lt;a href=&quot;https://github.com/acvetkov/sinon-chrome&quot;&gt;sinon-chrome&lt;/a&gt;, a library which enables mocking out these APIs.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;yarn&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sinon-chrome&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; --dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new module in the src directory called popup.js with &lt;a href=&quot;https://github.com/pureooze/extension-testing-example/blob/master/src/popup.js&quot;&gt;the following code&lt;/a&gt;. Notice how the getUrl function relies on the browser.runtime.getURL API. We are going to use sinon-chrome to mock the response the browser would return.&lt;/p&gt;
&lt;p&gt;Create a new file called popup.test.js in the test directory to store all the tests for the popup.js file you just created. Add the following code to the test file and notice how the browser API is mocked by sinon-chrome. For every test that uses the WebExtension APIs, you must specify what each API should return when the test runner (Chrome) encounters it, allowing you to bypass the issue with the APIs not being defined.&lt;/p&gt;
&lt;p&gt;Run yarn test and you should see the following results from the tests:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/NoXrskMxCq-885.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/NoXrskMxCq-885.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;And there you are, free to test your chrome extension without having to fear the extension APIs.&lt;/p&gt;
&lt;h2 id=&quot;future-work&quot; tabindex=&quot;-1&quot;&gt;Future Work &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2018-05-21-unit-testing-browser-extensions/#future-work&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While this setup with Karma, Jasmine and Chrome works, it is not an ideal solution. There are some benefits in using Jest, a modern testing library that runs entirely in Node thus eliminating the need for a test runner and browser. Unfortunately, Jest has some compatibility issues with Preact so for the time being I have put it on the back burner. Hopefully I can find some time to migrate the tests to use Jest because I think it will make for a good blog post.&lt;/p&gt;
</content>
	</entry>
	<entry>
		<title>Test Driven Development In JavaFX With TestFX</title>
		<link href="https://pureooze.com/blog/posts/2017-08-06-test-driven-development-in-javafx-with-testfx/"/>
		<updated>2017-08-06T00:00:00Z</updated>
		<id>https://pureooze.com/blog/posts/2017-08-06-test-driven-development-in-javafx-with-testfx/</id>
		<content xml:lang="en" type="html">&lt;p&gt;A dive into how to test JavaFX applications using TestFX.&lt;/p&gt;
&lt;p&gt;If you just want to read about how to use TestFX skip to the &amp;quot;Setting Up Our Application&amp;quot; section.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This post was originally posted on August 6th 2017. Things may have changed since then, and this post may no longer be accurate. Proceed with caution.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I recently started working at a company that has a very heavy reliance on Java applications.
As a primarily Python, Javascript and C++ developer, I had not used Java for a few years so given the obvious fact that I would have to get comfortable with using Java on a day to day basis, I decided to work on a small project in Java to get my feet wet.&lt;/p&gt;
&lt;h2 id=&quot;the-application&quot; tabindex=&quot;-1&quot;&gt;The Application &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2017-08-06-test-driven-development-in-javafx-with-testfx/#the-application&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While thinking about possible project ideas for this Java application I browsed through my Github repos and saw an all too familiar project, a podcast feed aggregator/player that I had worked on with some friends.
The application was written using Qt 5.8 and was capable of fetching RSS feeds for any audio podcast on iTunes, stream episodes and render the HTML contents associated to an individual episode.
The links shown below in the right hand side widget would also open in the default system browser when clicked on by a user.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/FYAQHcac7e-786.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/FYAQHcac7e-786.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Screenshot of the original PodcastFeed application. On the left is the podcast list, the middle is the episode list (contents change based on selected podcast) and the right is the description rendering for each individual episode.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;While the application looks pretty simple (and it is) the fun of developing this came from working with two friends to learn Qt while also developing the application quickly and in a small amount of time.
The reason I considered rewriting this application in Java was because the code we originally wrote had several flaws (keep in mind we developed it in a very short amount of time):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We wrote zero tests for the application (Yikes!!)&lt;/li&gt;
&lt;li&gt;Classes were an after thought, they were not part of the initial design&lt;/li&gt;
&lt;li&gt;Due to the above the functions were very tightly coupled&lt;/li&gt;
&lt;li&gt;Tight Coupling made it very difficult to modify core processes of the application (like XML parsing) without breaking other features&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-approach-tdd&quot; tabindex=&quot;-1&quot;&gt;The Approach - TDD &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2017-08-06-test-driven-development-in-javafx-with-testfx/#the-approach-tdd&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Given the flaws mentioned above it became clear to me that I would need to take a different approach to this new application if I wanted any hope of being able to maintain it for any reasonable amount of time.
For help with how to approach this application design I turned to a book I had been reading recently: &lt;em&gt;&lt;strong&gt;Growing Object-Oriented Software, Guided by Tests by Steve Freeman and Nat Pryce&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/geBKrCxhKC-382.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/geBKrCxhKC-382.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Tests are a core part of development, and this book is a great source of knowledge about how to create good tests.&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This book is a fantastic read if you want to learn about what makes Test Driven Development a powerful tool for use in Object Oriented development.
The authors provide a lot of deep insight on why tests are important, what kind of tests should be used when and how one writes expressive, easy to understand tests.
I will spare the details on TDD but you can google it or read the book above to learn more about it.&lt;/p&gt;
&lt;p&gt;The authors of the book continuously stress the importance of using End to End tests because of their ability to test the full development, integration and deployment processes in an organization.
I myself do not have automated build systems such as Atlassian’s Bamboo system so my automation capabilities are limited, but I can make use of Unit tests to test individual functions and UI Tests to try and get as much End to End coverage (between the local application and the podcast data on the Internet) as possible.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-our-application&quot; tabindex=&quot;-1&quot;&gt;Setting Up Our Application &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2017-08-06-test-driven-development-in-javafx-with-testfx/#setting-up-our-application&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Getting a JavaFX project working with TestFX 4 is actually quite simple but a lot of the challenges working with TestFX actually come from the lack of documentation by the TestFX developers.
TestFX 4 in particular has at the time of writing, almost no clear documentation available. Please note that for the rest of this post I will be using examples involving IntelliJ.
If you use a different IDE you may have to adjust certain steps to match your environment.&lt;/p&gt;
&lt;p&gt;To start off, lets create a JavaFX project in IntelliJ.
Once the project is created, press &lt;code&gt;Shift + F10&lt;/code&gt; to run it to make sure it is working.
If you get an error at this step you may have configured your Java/JavaFX environment incorrectly.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Main.java&lt;/code&gt; file is the one that will run at the very start of execution.
It will kick off the application as well as create any widgets and scenes we specify.
By default it looks something like this:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; sample&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;application&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Application&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;fxml&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;FXMLLoader&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Parent&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt; Main&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; Application&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; start&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Stage&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; primaryStage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; throws&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;        Parent&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; root&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; FXMLLoader&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getClass&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getResource&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;sample.fxml&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;        primaryStage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;setTitle&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;        primaryStage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;setScene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; Scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;root&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; 300&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; 275&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;        primaryStage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;show&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[]&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; args&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;        launch&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy the following into the sample.fxml file, this will serve as the widget layout for the application:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;xml&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; version&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; encoding&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; javafx&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;.scene.control.Button&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; javafx&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;.scene.control.Label&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; javafx&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;.scene.control.TextField&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; javafx&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;.scene.layout.AnchorPane&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; javafx&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;.scene.layout.ColumnConstraints&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; javafx&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;.scene.layout.GridPane&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; javafx&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;.scene.layout.RowConstraints&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;GridPane&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; alignment&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;center&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; hgap&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;10&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; vgap&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;10&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt;fx&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;http://javafx.com/fxml/1&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;http://javafx.com/javafx/8.0.111&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; fx&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt;controller&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;sample.Controller&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;   &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;TextField&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; layoutX&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;15.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; layoutY&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;25.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; fx&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;inputField&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;Label&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; layoutX&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;15.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; layoutY&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;84.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; text&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;TEXT GOES HERE&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; fx&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;label&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; layoutX&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;124.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; layoutY&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;160.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; mnemonicParsing&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; text&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;Apply&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; onAction&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#applyButtonClicked&quot;&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; fx&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;applyButton&quot;&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;   &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt;GridPane&lt;/span&gt;&lt;span style=&quot;color:#6E6A86&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now copy the following into the Controller.java file.
By binding the FXML file to the Controller with the use of the fx:controller tag, we can dictate behavior for our application using Java code.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; sample&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;ActionEvent&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;fxml&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;FXML&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;control&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;control&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;control&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;TextField&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt; Controller&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;FXML&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  TextField&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; inputField&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;FXML&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  Label&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;FXML&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  Button&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; applyButton&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; applyButtonClicked&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;    label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;setText&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;inputField&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getText&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the application again (press Shift + F10) and verify that entering text into the input field and then clicking the apply button causes it to be copied into the label like so:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/5hqnUP-BP1-313.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/5hqnUP-BP1-313.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;It is clear the application runs in a very basic use case so TestFX can now be used to create UI tests to validate this belief.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-testfx&quot; tabindex=&quot;-1&quot;&gt;Setting Up TestFX &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2017-08-06-test-driven-development-in-javafx-with-testfx/#setting-up-testfx&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To start using TestFX the first step is to import the library into a project.
IntelliJ makes this pretty easy, just click &lt;code&gt;File &amp;gt; Project Structure&lt;/code&gt;.
In the window that pops up, click &lt;code&gt;Libraries &amp;gt; the green plus sign &amp;gt; From Maven&lt;/code&gt;.
In the popup window search for testfx and select testfx-junit:4.0.6 like below and click OK:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/qwrnPhKjw6-1046.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/qwrnPhKjw6-1046.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Repeat the steps above and add &lt;em&gt;testfx-core:4.0.6&lt;/em&gt;, &lt;em&gt;hamcrest-junit:2.0.0.0&lt;/em&gt;, &lt;em&gt;junit:4.12&lt;/em&gt;, &lt;em&gt;loadui:testFx:3.1.2&lt;/em&gt; and &lt;em&gt;guava:14.0.1&lt;/em&gt; to the project.
You should have the following in the libraries pane now:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/G7_gu3Lsi_-320.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/G7_gu3Lsi_-320.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now we need to create a directory to place our tests in.
IntelliJ provides a really easy way to do this.
Right click the project name in the &lt;code&gt;Project Pane &amp;gt; New &amp;gt; Directory&lt;/code&gt; and name this new directory &lt;code&gt;tests&lt;/code&gt;.
Now right click this newly created directory and &lt;code&gt;Mark Directory As &amp;gt; Tests Sources Root&lt;/code&gt;.
Once that is done, open up &lt;code&gt;Main.java&lt;/code&gt; and click on the class name:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/d5gGw2blc0-743.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/d5gGw2blc0-743.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Press &lt;code&gt;ALT + Enter &amp;gt; Create Test&lt;/code&gt;.
Now you can configure what you want to use to run these tests but for now the default will suffice.
Click OK.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/Juz7T2l4XX-449.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/Juz7T2l4XX-449.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;There should now be a new file called MainTest.java in the tests directory.
This file will should house all the tests related to the Main.java file (ie. the full UI application).
To begin using TestFX we need to first make some changes to our test file.
Recall that in Main.java, the Main class extends the Application class.
Likewise when we want to use TestFX to perform testing of our Main class, we must extend the ApplicationTest class from the org.testfx.framework package.&lt;/p&gt;
&lt;p&gt;IntelliJ now warns us that the ApplicationTest class requires that we implement a start(Stage) method, so lets do that:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; start&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Stage&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; throws Exception &lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  Parent&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; mainNode&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; FXMLLoader&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;Main&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getResource&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;sample.fxml&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;  stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;setScene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; Scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;mainNode&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;  stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;show&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;  stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;toFront&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have to create mainNode in order to allow us to refresh the application after each test has run, this will ensure that we never have stale data from a previous test causing unexpected errors in our tests.
Now we should implement our setUp and tearDown methods, to tell TestFX what it should do before and after it has run a test.
Its important to do this because you want to ensure that your test environment is consistent with what you expect it to be “in the real world”.
In our case the setUp method is empty as we don’t need to prep anything before we run a test, but as applications get more complex it becomes vital to make use of this method.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Before&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; setUp&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; throws Exception &lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;After&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; tearDown&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; throws Exception &lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;  FxToolkit&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;hideStage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  release&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; KeyCode&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[]{});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  release&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; MouseButton&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[]{});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;writing-our-first-test&quot; tabindex=&quot;-1&quot;&gt;Writing Our First Test &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2017-08-06-test-driven-development-in-javafx-with-testfx/#writing-our-first-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At long last we can start writing a test! Lets begin with a simple one that just enters text into our inputField.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Click to see the code&lt;/summary&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; sample&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;fxml&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;FXMLLoader&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Parent&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;KeyCode&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;MouseButton&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import javafx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import org&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;junit&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;After&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import org&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;junit&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Before&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import org&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;junit&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Test&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import org&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;testfx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;api&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;FxToolkit&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import org&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;testfx&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;framework&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;junit&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;ApplicationTest&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;import static org&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;junit&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Assert&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#9CCFD8&quot;&gt; MainTest&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; ApplicationTest&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; start&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Stage&lt;/span&gt;&lt;span style=&quot;color:#C4A7E7;font-style:italic&quot;&gt; stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; throws&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;    Parent&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; mainNode&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; FXMLLoader&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;Main&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getResource&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;sample.fxml&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;    stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;setScene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; Scene&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;mainNode&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;    stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;show&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;    stage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;toFront&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Before&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; setUp&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; throws&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;After&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; tearDown&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; throws&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;    FxToolkit&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;hideStage&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;    release&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; KeyCode&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[]{});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;    release&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; MouseButton&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[]{});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; testEnglishInput&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;    clickOn&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#inputField&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;    write&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;This is a test!&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;Now we can try running this test by right clicking the tests directory and clicking Run ‘All Tests’ and you should see something like this:&lt;/p&gt;
&lt;video controls=&quot;&quot;&gt;
  &lt;source src=&quot;https://pureooze.com/video/2017-08-06-test-driven-development-in-javafx-with-testfx/test-running.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;Great! We just got our first UI test to work. Now lets actually make it test our applications functionality:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; testEnglishInput&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  Label&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;Label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; GuiTest&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#label&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  clickOn&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#inputField&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  write&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;This is a test!&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  clickOn&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#applyButton&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  assertThat&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getText&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(),&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;This is a test!&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This new version of the test will now click the Apply button after entering the text into our inputField and then attempt to assert that the label is actually set to the desired message (what we input) and not something else.
Running this test now confirms our belief that our application functions as expected, thus ensuring that if we were to update our code the test would help us validate the functionality.&lt;/p&gt;
&lt;p&gt;On a side note, notice that the assert statement is written in a format that reads very naturally in English. If we read it out we get something like &amp;quot;Assert that label.getText is MESSAGE&amp;quot;.
In the Growing Object Oriented book by Freeman and Pryce they discuss the importance of writing tests in this format to make them easy to read.&lt;/p&gt;
&lt;p&gt;To finish off lets write some more tests for two additional cases. We will test French and numerical inputs.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; testFrenchInput&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  Label&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;Label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; GuiTest&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#label&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  clickOn&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#inputField&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  write&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;C&#39;est un test!&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  clickOn&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#applyButton&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  assertThat&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getText&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(),&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;C&#39;est un test!&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;Test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; testNumericalInput&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  Label&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;Label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt; GuiTest&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#label&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  clickOn&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#inputField&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  write&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;123456789&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  clickOn&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;#applyButton&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  assertThat&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;getText&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(),&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;&quot;123456789&quot;&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when we run all our tests TestFX will automatically reset our application state so that our changes from the previous test do not cause errors when then next test begins.&lt;/p&gt;
&lt;video controls=&quot;&quot;&gt;
  &lt;source src=&quot;https://pureooze.com/video/2017-08-06-test-driven-development-in-javafx-with-testfx/test-running2.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;As we can see TestFX is a very powerful framework for testing JavaFX applications.
While it does lack documentation, TestFX has in my own experience provided a lot of value when I am trying to ensure that the feature of my application are working as intended.
Compared to my experiences in developing a UI with Qt and no UI Testing, this project with JavaFX and TestFX has been a far more enjoyable one.&lt;/p&gt;
</content>
	</entry>
	<entry>
		<title>What Is PAM?</title>
		<link href="https://pureooze.com/blog/posts/2016-08-07-what-is-pam/"/>
		<updated>2016-08-07T00:00:00Z</updated>
		<id>https://pureooze.com/blog/posts/2016-08-07-what-is-pam/</id>
		<content xml:lang="en" type="html">&lt;p&gt;The last post I did was the start of the Comprehensive Guide To AppArmor which took a look at the basics an administrator or developer needs to know to start creating and deploying AppArmor profiles for a program.
In the post I also left a question for the reader regarding AppArmor being used to replace the traditional DAC permissions (but never should!) and how you could use it to remove access to a file from a specific user (rather than a program).
However, this requires usage of the &lt;code&gt;pam_apparmor&lt;/code&gt; module for PAM and due to this, before going into depth with using &lt;code&gt;pam_apparmor&lt;/code&gt;, you should make sure you have a grasp of the basics of PAM and its configuration files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This post was originally posted on August 7th 2016. Things may have changed since then, and this post may no longer be accurate. Proceed with caution.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;seriously-what-is-pam&quot; tabindex=&quot;-1&quot;&gt;Seriously What Is PAM? &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#seriously-what-is-pam&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PAM stands for Pluggable Authentication Modules and is used to perform various types of tasks involving authentication, authorization and some modification (for example password change).
It allows the system administrator to separate the details of authentication tasks from the applications themselves.
This allows the policy to not only be generic, it means that the programs do not need to be modified in order to update the policy!
An example of PAM usage is controlling login attempts to a shell/GUI interface so that only successful authentication and authorized events are allowed.
You could also use PAM to control who can use the su binary to switch identities or control who can use the passwd utility to change passwords.&lt;/p&gt;
&lt;h3 id=&quot;overview&quot; tabindex=&quot;-1&quot;&gt;Overview &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#overview&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a developer wishes to interact with PAM to let it handle events, they must include libpam which allows communication via the API provided by the library.
When PAM sees a new event that it must process, it will look at the relevant configuration files found in /etc/pam.d and determine which modules must be used at certain stages.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/JrsUhab9VT-550.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/JrsUhab9VT-550.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Source: http://www.tuxradar.com/content/how-pam-works&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;PAM is capable of using context to determine what it needs to do, for example the &lt;code&gt;pam_unix.so&lt;/code&gt; module has capabilities for the auth and account stack.
In the auth stack it checks a username and password combo while in the account stack it will check a users aging and expiration info.
This versatility is one of the reasons PAM has been so popular in the UNIX world, it allows for solutions that can be combined to create a generic library to deal with certain type of request.&lt;/p&gt;
&lt;h3 id=&quot;how-do-i-tell-a-program-supports-pam&quot; tabindex=&quot;-1&quot;&gt;How Do I Tell A Program Supports PAM? &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#how-do-i-tell-a-program-supports-pam&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is usually pretty easy, you can use ldd to check if libpam is in use:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;comp:/home&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; ldd /usr/sbin/sshd | grep pam&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   libpam.so.0&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /lib64/libpam.so.0&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; (0x02209ddace0105400)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;   &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;comp:/home&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; ldd /bin/su | grep pam&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   libpam.so.0&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /lib64/libpam.so.0&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; (0x02999ddace0105400)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   libpam_misc.so.0&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /lib64/libpam_misc.so.0&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; (0x12211ddace1105400)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;configuration&quot; tabindex=&quot;-1&quot;&gt;Configuration &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#configuration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I already mentioned, PAM configuration files are stored in &lt;code&gt;/etc/pam.d&lt;/code&gt; for all valid programs.
A line in a configuration file will look something like this:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sufficient&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_rootok.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are three parts to this line, the first is called the &lt;code&gt;function type&lt;/code&gt; which is the PAM function (stack) that an application asks PAM to perform.
The second is a &lt;code&gt;control argument&lt;/code&gt; which is used to determine how PAM responds to a success or fail when performing the action.
The final part is the &lt;code&gt;module&lt;/code&gt; which PAM calls when it reaches this line, it will determine what PAM specifically does to validate the auth attempt.&lt;/p&gt;
&lt;h3 id=&quot;function-types&quot; tabindex=&quot;-1&quot;&gt;Function Types &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#function-types&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are four possible functions that can be called inside a configuration file.
Each of these provides a unique context that can be used within the module.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;auth&lt;/td&gt;
&lt;td&gt;Authenticate a user, make sure they are who they say they are&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;account&lt;/td&gt;
&lt;td&gt;Check user account status, ensure they are authorized to do action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;session&lt;/td&gt;
&lt;td&gt;Perform something for the users session like allocate resources they might need, for example mounting their home directory or setting usage limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;password&lt;/td&gt;
&lt;td&gt;Change a users password or other creds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;control-arguments&quot; tabindex=&quot;-1&quot;&gt;Control Arguments &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#control-arguments&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Understanding control arguments is a critical part of creating a secure PAM policy, if you mess up the order you pay the price!
There are five types of simple control arguments:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Argument&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;sufficient&lt;/td&gt;
&lt;td&gt;if it succeeds then auth is successful and PAM does not need to look at more rules (assuming no required module failed), if it fails then keep going&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requisite&lt;/td&gt;
&lt;td&gt;if it succeeds PAM proceeds to additional rules, if it fails then PAM stops and sends a failure message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;required&lt;/td&gt;
&lt;td&gt;if it succeeds then proceed to next line, if it fails then proceeds to additional rules but will always return an unsuccessful auth regardless of end result. Useful because it will not say what caused the failure, thus an attacker will not be able to know if they got some part of the auth right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;optional&lt;/td&gt;
&lt;td&gt;the result is ignored, only becomes necessary for successful auth when no other modules reference the function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;include&lt;/td&gt;
&lt;td&gt;pulls in all lines in the config file which match the given param and appends them as arguments to the module&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;module-arguments&quot; tabindex=&quot;-1&quot;&gt;Module Arguments &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#module-arguments&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PAM is also capable of taking arguments and passing them to the target module.
This allows an administrator to specify constraints like min/max length for passwords or if a module should run in debug mode.
The following is an example of how you can pass arguments to a module:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sufficient&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; nullok&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see all you need to do is append the line with the values you wish to pass to the module.
In this case the value nullok tells pam_unix that it is okay to be supplied no password at all by the user.
In the case you wish to pass more than one argument to the module you can simply separate them using spaces like so:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; optional&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_apparmor.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; order=user,group,default&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; debug&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;simple-example&quot; tabindex=&quot;-1&quot;&gt;Simple Example &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#simple-example&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that you know what the contents of a PAM file mean, lets look at a simple example from the book How Linux Works (2nd Edition) by Brian Ward.
The following is a PAM config (albiet old) for the chsh utility:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sufficient&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_rootok.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; requisite&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_shells.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sufficient&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_deny.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first line says that if the user attempting the action is root then that is enough authority for PAM, and it will consider the authentication successful and end.
If the user is not root then PAM will continue to the next line.&lt;/p&gt;
&lt;p&gt;The second line checks to see if the shell the user is attempting to switch to is listed in /etc/shells.
If it is not listed there then PAM considers this an authentication failure and immediately quits.
It will also output an error message stating why the failure occured. If the shell is listed in /etc/shells then PAM continues to the next line.&lt;/p&gt;
&lt;p&gt;The third line attempts to authenticate a user via their password.
If the user provides the correct password then PAM will consider the authentication successful and end.
If however the password is wrong, then PAM will continue to the fourth and final line.&lt;/p&gt;
&lt;p&gt;The fourth line in this sample file may look the same as all the rest, but it is actually special.
The pam_deny module is what can be called a default deny module, it will always return an authentication failure no matter what function or control arguments call it.
This is similar to the idea of how firewalls are configured with a default deny rule at the very end of the chain so that any packet which does not match a previous rule is dropped.
In this case PAM will have an authentication failure and will not explain why it failed.
This is a very important part of making PAM configs, make sure you have the default deny policy at the end and use the required control argument so that an adversary cannot tell what caused the failure.&lt;/p&gt;
&lt;p&gt;In case it helps, the author was kind enough to provide a flow chart to illustrate how PAM will deal with this module:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;
&lt;picture&gt;
&lt;source srcset=&quot;https://pureooze.com/img/ZPBlRM_f24-567.avif&quot; type=&quot;image/avif&quot; /&gt;
&lt;img class=&quot;post-image&quot; src=&quot;https://pureooze.com/img/ZPBlRM_f24-567.webp&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption&gt;Source: How Linux Works (2nd Edition) by Brian Ward&lt;/figcaption&gt;
&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3 id=&quot;practice&quot; tabindex=&quot;-1&quot;&gt;Practice &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#practice&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Here is another example of a PAM config file:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_securetty.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; nullok&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_nologin.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;account&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;password&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_cracklib.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; retry=&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;password&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; shadow&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; nullok&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; use_authtok&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Try and write out/draw what PAM will do for this file, don’t worry too much about what the modules that are called do but focus on what each function and control argument try to do.
Once you think you have it done, continue reading.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;So I hope you saw that since the config file above contains only the required control argument, if there is ever a failure with one of the modules the user will not know which specific step caused the failure.
This can be useful is cases when we do not want the adversary to be able to tell which of their actions caused the failure, making it harder for them to tell what they did right and what they did wrong (for example if they guessed a password, path or ID correctly).&lt;/p&gt;
&lt;h3 id=&quot;the-big-kahuna&quot; tabindex=&quot;-1&quot;&gt;The Big Kahuna &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-08-07-what-is-pam/#the-big-kahuna&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s look at a modern &lt;code&gt;chsh&lt;/code&gt; config for PAM:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;%PAM-1.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sufficient&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_rootok.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; include&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; common-auth&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;account&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; include&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; common-account&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;password&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; include&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; common-password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; include&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; common-session&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recall the function of the include control argument:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;include&lt;/strong&gt; — pulls in all lines in the config file which match the given param and appends them as arguments to the module&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means that once PAM has finished processing the include lines, the file ends up looking like this (only in memory, file on disk is not written to):&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;%PAM-1.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sufficient&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_rootok.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; common-auth&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_env.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; optional&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_gnome_keyring.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;     try_first_pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; common-account&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;account&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; try_first_pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; common-password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;password&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; requisite&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_cracklib.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;password&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; optional&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_gnome_keyring.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; use_authtok&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;password&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; use_authtok&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; nullok&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; shadow&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; try_first_pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; common-session&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_limits.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; required&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_unix.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; try_first_pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; optional&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_umask.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; optional&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_systemd.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; optional&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_gnome_keyring.so&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; auto_start&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; only_if=gdm,gdm-password,lxdm,lightdm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; optional&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; pam_env.so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like the previous exercise, try and write out/draw what the flow of the PAM module will be given these modules.
Remember that the required control argument only outputs a failure at the end of the run.
This prevents the adversary from being able to know what step failed (no error message) and it prevents them from being able to perform a timing attack to attempt a guess at the failed step.
If you are able to map out the flow of this module then you have a good grasp of PAM and should feel confident approaching any other modules on a modern Linux system!&lt;/p&gt;
</content>
	</entry>
	<entry>
		<title>The Comprehensive Guide To AppArmor: Part 1</title>
		<link href="https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/"/>
		<updated>2016-07-28T00:00:00Z</updated>
		<id>https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/</id>
		<content xml:lang="en" type="html">&lt;p&gt;I wanted to do this post on the basics of AppArmor and how to get started with using it on your system.
This post started as a very small guide on AppArmor but as I wrote it I felt more and more convinced it needed details to explain various features and issues.
As such it has now ended up as a comprehensive guide on how to start using and understanding the AppArmor tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This post was originally posted on July 28th 2016. Things may have changed since then, and this post may no longer be accurate. Proceed with caution.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In case you don&#39;t know what AppArmor is, the official wiki provides a decent explanation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AppArmor is an mandatory access control (MAC) like security system for Linux.
It is designed to work with standard Unix discretionary access control (DAC) permissions while being easy to use and deploy, by allowing an admin to confine only specific applications.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Essentially AppArmor provides MAC functionality to Linux and is used to supplement the traditional DAC (file permissions) functionality that the OS provides.
Using the most basic AppArmor tools an administrator can create and deploy AppArmor profiles to restrict access for specific processes.
For example one could restrict the web browser to only let users access files in their home directories.
This would prevent a scenario where Alice would try to upload or share files owned by Bob without his knowledge.&lt;/p&gt;
&lt;h2 id=&quot;getting-started&quot; tabindex=&quot;-1&quot;&gt;Getting Started &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#getting-started&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To run AppArmor the first step is the same as all other software, make sure that it is installed.
OpenSUSE and Ubuntu have it installed and enabled by default but other distros may vary.
Installing AppArmor is usually as simple as checking if a distro has a package for it, then downloading and installing the package.
Note that the kernel must be compiled with support for AppArmor.
Once AppArmor is running you can make sure the service is running using:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;systemctl&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; status&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; apparmor&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;  #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; Checks status of the AppArmor service and tells you if it is enabled on boot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;systemctl&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; start&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; apparmor&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;  #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; Starts the service&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;systemctl&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; enable&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; apparmor&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;  #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; Makes apparmor start on boot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will ensure that the system is always up and running with AppArmor ready to enforce profiles.&lt;/p&gt;
&lt;h3 id=&quot;using-aa-status&quot; tabindex=&quot;-1&quot;&gt;Using aa-status &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#using-aa-status&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now that the service is running let&#39;s start using the AppArmor tools (&lt;code&gt;aa-tools&lt;/code&gt;) to monitor and manage the system.
To start we can use the &lt;code&gt;aa-status&lt;/code&gt; tool to check what profiles are loaded and what status they currently have.
On a fresh Ubuntu system I had something like this:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;user@ae:~$&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sudo&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; aa-status&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; password &lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; user:  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;apparmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; loaded.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; loaded.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; enforce&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; mode.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /sbin/dhclient&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /usr/bin/lxc-start&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /usr/bin/ubuntu-core-launcher&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /usr/lib/NetworkManager/nm-dhcp-client.action&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /usr/lib/NetworkManager/nm-dhcp-helper&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /usr/lib/connman/scripts/dhclient-script&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /usr/lib/lxd/lxd-bridge-proxy&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /usr/sbin/tcpdump&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   lxc-container-default&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   lxc-container-default-cgns&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   lxc-container-default-with-mounting&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   lxc-container-default-with-nesting&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; complain&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; mode.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; processes&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; have&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; defined.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; processes&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; enforce&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; mode.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;   /sbin/dhclient&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; (2092)  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; processes&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; complain&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; mode.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; processes&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; unconfined&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; but&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; have&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; a&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profile&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; defined.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So right away we can see that 12 profiles have been loaded by the system and are in an enforce status.
What this means is that AppArmor will monitor the processes that match these profiles and decide if a specific action is permitted or denied by the policy.
If this seems confusing so far, don&#39;t give up yet, once I show some examples on what profiles look like I think this will make more sense.&lt;/p&gt;
&lt;p&gt;One other thing I wanted to point out about this output was the &lt;code&gt;complain&lt;/code&gt; and &lt;code&gt;unconfined&lt;/code&gt; status.
When a profile is in complain mode AppArmor will allow it to perform (almost)&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; all tasks without restriction, but it will log them in the audit log as events.
This is useful when you are attempting to create a profile for an application but are not sure what things it needs access to.
The unconfined status on the other hand will allow the program to perform any task and will not log it.
This usually occurs if a profile was loaded after an application is started, meaning it runs without restrictions from AppArmor.
It&#39;s also important to note that only processes that have profiles are listed under this unconfined status, any other processes that run on your computer but do not have a profile created for them will not be listed under &lt;code&gt;aa-status&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;using-aa-genprof&quot; tabindex=&quot;-1&quot;&gt;Using aa-genprof &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#using-aa-genprof&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now that we covered what different states an AppArmor profile can be in we can move on to learning how to build these profiles for a specific program.
For this demo we are going to create a simple script that we will then enforce with AppArmor.
You will also need to create a directory called &amp;quot;data&amp;quot; in the same directory as the script before running it for this example to work.
The goal is to demonstrate that we can use AppArmor to prevent the script from accessing any other paths.&lt;/p&gt;
&lt;p&gt;So, create the directory &amp;quot;data&amp;quot; and then create a script called example.sh:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#!&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;/bin/bash &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;This is an apparmor example.&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;touch&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; data/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;File created&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;rm&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; data/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;File deleted&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to give it the execute permission and then run it:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;user@ae:~$&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; ./example.sh&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;This&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; apparmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; example.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; created&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; deleted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great our script worked! Now lets start enforcing it with AppArmor.
There are two main tools we use to generate and add on to profiles, &lt;code&gt;aa-genprof&lt;/code&gt; and &lt;code&gt;aa-logprof&lt;/code&gt;.
The first is used to monitor and create a profile for an application the first time it is run (i.e. when you are first creating the profile) so that AppArmor can learn what the applications tendencies are and prompt you for what behavior needs to be taken in certain circumstances.
The second is useful once you have an existing profile and need to allow/deny access to certain tasks which have already been logged during enforce or complain modes.&lt;/p&gt;
&lt;p&gt;Lets start &lt;code&gt;aa-genprof&lt;/code&gt; on our script:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;user@ae:~$&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; aa-genprof&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; example.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; ...long message about aa-genprof...&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Profiling:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; example.sh&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;can system log for AppArmor events&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;F&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)inish&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Leave this prompt as is, now run the example.sh script in a new terminal window.
Note that &lt;code&gt;aa-genprof&lt;/code&gt; requires us to run the program while it is monitoring the process so that it can scan the log to learn about all the events the program creates.
It will then propose rules to add to the profile for us.
Once you have run the script, press the &#39;s&#39; key in the genprof terminal window:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;can system log for AppArmor events&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;F&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)inish &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Reading&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; entries&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /var/log/audit/audit.log.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Updating&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; AppArmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /etc/apparmor.d.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Profile:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;  /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Execute:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;  /usr/bin/touch&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Severity:&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;I&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;nherit&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; (C)hild / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;N&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)amed / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;X&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;) ix On / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)eny / Abo(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)t / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;F&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)inish&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s stop for a second here and get a grasp of what exactly we are looking at.
This is what a prompt from the AppArmor tools looks like, it is telling you that a profile (usually denoted by the absolute path of the program) is attempting to access or execute a certain file OR that the process requires access to a certain capability from the kernel.
If you are not familiar with capabilities don&#39;t worry about them for now.&lt;/p&gt;
&lt;p&gt;AppArmor also attempts to provide a severity warning based on what the process is trying to do (1 being low warning and 10 being very serious) but this is not always accurate as some programs legitimately need access to resources which count as a very severe warning.
For example the Chrome(ium) browser requires access to &lt;code&gt;CAP_SYS_ADMIN &lt;/code&gt;to create a secure sandbox for its child processes but AppArmor would give a severity 10 warning for this.&lt;/p&gt;
&lt;p&gt;We should also note how AppArmor gives us several different options here on what we can do but let&#39;s focus on the core ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Inherit:&lt;/strong&gt; Creates a rule that is denoted by &amp;quot;ix&amp;quot; within the profile, causes the executed binary to inherit permissions from the parent profile.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Child:&lt;/strong&gt; Creates a rule that is denoted by &amp;quot;Cx&amp;quot; within the profile, requires a sub-profile to be created within the parent profile and rules must be separately generated for this child (prompts will appear when running scans on the parent).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deny:&lt;/strong&gt; Creates a rule that AppArmor prepends with &amp;quot;deny&amp;quot; at the start of the line within the profile, causes the parents access to the resource be denied.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Abort:&lt;/strong&gt; Exits the AppArmor program without saving any changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Finish:&lt;/strong&gt; Exits the AppArmor program but WILL save changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is really the tricky part with generating your own AppArmor profiles, you need to know what permissions the need to be given and denied.
Due to this I highly recommend using preexisting profiles if it is at all possible, especially if they are peer-reviewed and distributed by a trusted party.
For this script however we are fine with just giving /usr/bin/touch Inherit permissions.
After that a similar prompt will come up for /usr/bin/rm and since we know the script also legitimately uses that, we will give Inherit access once again.&lt;/p&gt;
&lt;p&gt;The next prompt seems a bit different than these previous two:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Complain-mode&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; changes:&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Profile:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;  /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Path:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;     /dev/tty&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Mode:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;     rw&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Severity:&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt; 9&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  1&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/consoles&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  2&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/libvirt-qemu&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  3&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/ubuntu-konsole&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  4&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/ubuntu-xterm&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; - /dev/tty&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;llow&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)eny / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;I&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)gnore / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;G&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)lob / Glob with (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;E&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)xtension / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;N&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)ew / Abo(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)t / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;F&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)inish / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;M&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)ore&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how there is a new field called &amp;quot;Mode&amp;quot; that tells us what permissions the script is requiring when accessing the &amp;quot;Path&amp;quot;.
Also note how there are some new options available, the core ones are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Allow:&lt;/strong&gt; Allow access to the path with the requested permissions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deny:&lt;/strong&gt; Deny access to the path with the requested permissions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ignore:&lt;/strong&gt; Skip this prompt, it will appear again the next time you run logprof&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since this script is prints to the console it needs access to the TTY interface and so it can be allowed.
Another prompt follows for /etc/ld.so.preload which according to the man page for ld.so is used for &amp;quot;list of ELF shared objects to be loaded before the program&amp;quot;.
This can be allowed as it is harmless. The next prompt shows something interesting, see if you can recognize its importance:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Profile:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;  /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Path:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;     /home/user/bin/data/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Mode:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;     rw&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Severity:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; unknown&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; - /home/user/bin/data/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;llow&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)eny / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;I&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)gnore / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;G&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)lob / Glob with (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;E&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)xtension / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;N&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)ew / Abo(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)t / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;F&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)inish / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;M&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)ore&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should look familiar as this is the file that is created by the script!
AppArmor is notifying us that example.sh tries to create a file sample.txt in the directory /home/user/bin/data.
Once again we know this is legitimate behavior for the script, and so we can allow it.
Finally, we get a prompt asking if we wish to save changes or not. Press &#39;s&#39; to save changes and then &#39;f&#39; to finish.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Changed&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Local&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;The&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; following&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; local&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; were&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; changed.&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Would&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; you&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; like&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; save&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; them?&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; - /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;ave&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Changes&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Save&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Selec&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;ed&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Profile&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; [(V)iew Changes] / View Changes b/w (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;C&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)lean profiles / Abo(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)t &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Writing&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; updated&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profile&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /home/user/bin/example.sh.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Profiling:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;[(&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4;font-style:italic&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;can system log for AppArmor events&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;F&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)inish &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Reloaded&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; AppArmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; enforce&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; mode.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Please&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; consider&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; contributing&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; your&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profile!&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;See&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; following&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; wiki&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; page&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; more&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; information:&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;http://wiki.apparmor.net/index.php/Profiles&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Finished&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; generating&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profile&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /home/user/bin/example.sh.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Confirm the script still works by running it again, AppArmor should not have interfered with the script.&lt;/p&gt;
&lt;h2 id=&quot;learning-to-adapt&quot; tabindex=&quot;-1&quot;&gt;Learning To Adapt &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#learning-to-adapt&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Up to this point we have seen how to use AppArmor to profile and enforce rules against a process.
However, this was all done under the assumption that the example.sh script will never change.
Unfortunately software in the real world does not work this way, it is often updated which causes it to require addition or removal of access for certain files and features.
This means we need to be able to leverage AppArmor, so it allows us to update profiles as programs change.
This is where &lt;code&gt;aa-logprof&lt;/code&gt; and &lt;code&gt;aa-mergeprof&lt;/code&gt; come in.&lt;/p&gt;
&lt;h3 id=&quot;aa-logprof&quot; tabindex=&quot;-1&quot;&gt;aa-logprof &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#aa-logprof&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This tool is used to scan the audit log for any events that AppArmor could not match to existing rules in a profile and will then prompt you for changes.
Before we start using it, make sure that the example.sh profile is being enforced by AppArmor.
You can check this using &lt;code&gt;aa-status&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;apparmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; loaded.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;???&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; profiles are loaded. &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;???&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; profiles are in enforce mode.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;/home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;---&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; You&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; should&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; see&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; it&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; under&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; heading&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;???&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; profiles are in complain mode.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;???&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; processes have profiles defined. &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;???&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; processes are in enforce mode.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;???&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; processes are in complain mode. &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt;???&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; processes are unconfined but have a profile defined.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now open up the script and edit it so that instead of using the data directory, the script attempts to read and write in its own directory:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#!&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;/bin/bash  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;This is an apparmor example.&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;touch&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sample.txt&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;File created&quot;&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;rm&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; sample.txt&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &quot;File deleted&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then just run the script and you should see an error message similar to this:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;This&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; apparmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; example.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;touch:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; cannot&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; touch&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &#39;sample.txt&#39;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Permission&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; denied&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; created&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;rm:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; cannot&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; remove&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &#39;sample.txt&#39;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; No&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; such&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; file&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; or&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; directory&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; deleted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Success! As strange as it may seem, this is good news because AppArmor has blocked the script from making changes to the system since it no longer matches the rules described in the profile.
To fix this we need to have the AppArmor profile updated, so start &lt;code&gt;aa-logprof&lt;/code&gt; and follow the prompts to allow access to the new paths and save changes:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;user@ae:~$&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; aa-logprof&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Reading&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; entries&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /var/log/audit/audit.log.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Updating&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; AppArmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /etc/apparmor.d.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Enforce-mode&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; changes:&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Profile:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;  /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Path:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;     /home/user/bin/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Mode:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;     w&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Severity:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; unknown&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; - /home/user/bin/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;llow&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; [(D)eny] / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;I&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)gnore / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;G&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)lob / Glob with (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;E&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)xtension / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;N&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)ew / Abo(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)t / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;F&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)inish / (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;M&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)ore &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Adding&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /home/user/bin/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; w&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Changed&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Local&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;The&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; following&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; local&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profiles&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; were&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; changed.&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Would&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; you&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; like&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; save&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; them?&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; - /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;ave&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Changes&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Save&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Selec&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#908CAA&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt;ed&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Profile&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; [(V)iew Changes] / View Changes b/w (&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;C&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)lean profiles / Abo(&lt;/span&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;)t &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;Writing&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; updated&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; profile&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /home/user/bin/example.sh.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now if the script is run, it completes successfully just like before:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;user@ae:~$&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; ./example.sh&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;This&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; apparmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; example.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; created&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; deleted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just like that have updated our scripts profile!&lt;/p&gt;
&lt;h3 id=&quot;aa-mergeprof-and-apparmor-profiles&quot; tabindex=&quot;-1&quot;&gt;aa-mergeprof &amp;amp; AppArmor Profiles &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#aa-mergeprof-and-apparmor-profiles&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this section I am not going to cover the technical details of &lt;code&gt;aa-mergeprof&lt;/code&gt; as it is not a key tool with day to day AppArmor usage.
Instead, I will provide a scenario and then explain what different things we see inside an AppArmor profile.
It is more important to understand the profiles than it is being familiar with the less common tools.&lt;/p&gt;
&lt;p&gt;Suppose we are in a situation where we have two systems A and B, along with two users Alice and Bob.
When Alice updates the script on computer A, she makes the appropriate changes to the AppArmor profile.
Bob at the same time makes different changes to the script and also updates the profile as appropriate.
At some later date Alice and Bob combine their versions of the script together and now want to use AppArmor to enforce a policy on it.
However, since they made changes to the profile separately they cannot just copy one of the profiles alone, they need to merge them together.
This may seem like a very difficult task, especially for very large scripts but fortunately &lt;code&gt;aa-mergeprof&lt;/code&gt; is able to save us from having to do most of the work!&lt;/p&gt;
&lt;p&gt;Now lets take a look at the actual profiles. All AppArmor profiles will be stored in the &lt;code&gt;/etc/apparmor.d&lt;/code&gt; directory.
If the example.sh is stored in &lt;code&gt;/home/user/bin/example.sh&lt;/code&gt; then you will find its respective profile in &lt;code&gt;/etc/apparmor.d/home.user.bin.example.sh&lt;/code&gt;.
Open that up in a text editor and let&#39;s see what it says:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; Last Modified: Wed Jul 27 23:19:01 2016 &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;tunables/global&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;/home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;  #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/base&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;  #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/bash&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /bin/bash&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; ix,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /dev/tty&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; rw,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /etc/ld.so.preload&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; r,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /home/user/bin/data/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; rw,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; r,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /home/user/bin/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; w,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /usr/bin/rm&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; rix,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /usr/bin/touch&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; rix,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below is what a profile declaration looks like, this tells AppArmor the path to the program and that anything within the {} is designated for this process.&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;/home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EB6F92;font-style:italic&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then the following lines are used to pull in generic rules from some preconfigured libraries:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/base&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/bash&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that there are a bunch of lines that define a file as well as the permissions the profile will allow them to access.
Don&#39;t worry too much about what they do individually and instead focus on the fact that they are single lines to describe access to a single resource.&lt;/p&gt;
&lt;p&gt;Let&#39;s reverse the changes we made before and force the script to require the data directory only once more.
Recall that to deny permission to a resource AppArmor will prepend the line with deny.
You will need to change the current profile so that it will deny access to the &lt;code&gt;/home/user/bin/sample.txt&lt;/code&gt; file AND delete the line for &lt;code&gt;/home/user/bin/data/sample.txt&lt;/code&gt;.
Once done it should look like this:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt; Last Modified: Wed Jul 27 23:19:01 2016 &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;tunables/global&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;/home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;  #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/base&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#908CAA;font-style:italic&quot;&gt;  #&lt;/span&gt;&lt;span style=&quot;color:#6E6A86;font-style:italic&quot;&gt;include &amp;#x3C;abstractions/bash&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /bin/bash&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; ix,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /dev/tty&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; rw,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /etc/ld.so.preload&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; r,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /home/user/bin/example.sh&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; r,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  deny&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /home/user/bin/sample.txt&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; w,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /usr/bin/rm&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; rix,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;  /usr/bin/touch&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; rix,&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One thing I should point out is that whenever you make manual changes to an AppArmor profile you need to tell AppArmor to do a refresh or else your changes will not take effect.
This can be done either through a command (ideal) or just by restarting the AppArmor service (not ideal).&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;apparmor_parser&lt;/span&gt;&lt;span style=&quot;color:#3E8FB0&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; /etc/apparmor.d/home.user.bin.example.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you try to run the script now you should get the same error we had before:&lt;/p&gt;
&lt;pre class=&quot;shiki rose-pine-moon&quot; style=&quot;background-color:#232136;color:#e0def4&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;This&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; apparmor&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; example.&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;touch:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; cannot&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; touch&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &#39;sample.txt&#39;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; Permission&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; denied&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; created&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;rm:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; cannot&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; remove&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; &#39;sample.txt&#39;:&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; No&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; such&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; file&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; or&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; directory&lt;/span&gt;&lt;span style=&quot;color:#E0DEF4&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA9A97&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#F6C177&quot;&gt; deleted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Awesome! This is proof that the changes made to our policies not only have a legitimate impact on the access control of the system, but they can be changed on the fly as needed.
There are more delicate details that we can explore about profiles but for an introduction this much is sufficient.&lt;/p&gt;
&lt;h2 id=&quot;going-to-the-next-level&quot; tabindex=&quot;-1&quot;&gt;Going To The Next Level &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#going-to-the-next-level&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Up to this point you have done enough with AppArmor that you can use it on a day-to-day basis to manage profiles on a single system.
However, in large or constantly changing environments the tools used so far are not enough, we need to be able to scale our monitoring and management of profiles as well as learn how to differentiate good policies from bad policies.
For now, I want to leave you with an interesting thought about AppArmor and we will try and address it in the part 2 of this guide.&lt;/p&gt;
&lt;h3 id=&quot;an-interesting-idea&quot; tabindex=&quot;-1&quot;&gt;An Interesting Idea &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#an-interesting-idea&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Recall the description of AppArmor from the official wiki:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AppArmor is an mandatory access control (MAC) like security system for Linux.
It is designed to work with standard Unix discretionary access control (DAC) permissions while being easy to use and deploy, by allowing an admin to confine only specific applications.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Recall that DAC can be used to restrict access to files and directories through a set of permissions defined by the owner of the file (and to some extent the system defaults).
The question we can ask is that if AppArmor is capable of restricting access to files and directories is it enough to just use it to completely replace our DAC system?
Consider the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Suppose Bob had created some confidential file that he did not want to share with Alice but placed it in a path that she had full access to.
Now imagine that he realized this and decided to use only AppArmor to prevent her and any other user from accessing it.
Could it be done? Can Bob guarantee that other users would never be able to have unauthorized access to the file?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;additional-notes&quot; tabindex=&quot;-1&quot;&gt;Additional Notes &lt;a class=&quot;header-anchor&quot; href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#additional-notes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;complain mode status will still enforce any explicit deny rules in a profile &lt;a href=&quot;https://pureooze.com/blog/posts/2016-07-28-the-comprehensive-guide-to-apparmor-p1/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
	</entry>
</feed>
