Tuesday, February 3, 2026

Learning Linux Part 4: So Many Distributions

There are over 1000 different or distributions (distros) of Linux.  Like every other person looking at the world of Linux it is overwhelming which distro to pick.  This blog post is not about picking a distro but explaining the major branches.  I am in the process of picking a distribution.  I will give you my recommendation.  These are the current top 10 most popular Linux distributions.

GNU / Linux

├── Debian

│   │

│   ├── Ubuntu

│   │   │

│   │   ├── Linux Mint

│   │   ├── Zorin OS

│   │   └── Pop!_OS

│   │

│   └── MX Linux

├── Fedora Workstation

└── Arch Linux

    │

    ├── Manjaro

    │

    ├── EndeavourOS

    │

    └── CachyOS



It may also help to view it as a Periodic Table of Distributions


This page lists all of the distributions in one hard to read branching graph:

These are the major distributions:

This is useful to see which distributions are the most popular by activity:

This is useful to see which distributions are rated the highest:

I have created a handy dandy cheat sheet:

Video Resources


Every Linux Distro Explained in 13 Minutes

EVERY LINUX DISTRO Explained in 60 Seconds!

Every LINUX DISTRO Explained in 4 minutes



Monday, February 2, 2026

Learning Linux Part 3: Linux on the Raspberry PI 5

The official Linux distribution for the Raspberry PI 5 is the Raspberry PI OS which is available in the Raspberry PI Imager.  It is based on Debian Trixie.

You would think that any ARM distribution would run on the Raspberry PI 5 but you would be wrong.  I tried downloading the ARM distribution of Ubuntu on Ubuntu.com.  I was only able to get the images that are part of the Raspberry PI Imager to work.  So I installed Ubuntu from the Raspberry PI Imager.  

I had tried  Ubuntu earlier with a Raspberry PI 4 with 4GB of RAM and it was so slow it was hard to navigate.  I had hopes that with a Raspberry PI 5 with 8GB of RAM and also a 1 Terabyte M.2 on my Pironman 5 that I would achieve performance enough where I could take this glorious RGB lit computer with me to show off to my friends at work and also maybe use it as a media server.  



I even made the Raspberry PI with Ubuntu look like Windows 11.

Windows 11 Theme

https://www.youtube.com/watch?v=qZqO0EY_yB8



I was let down with the performance.  When I tried watching YouTube videos with my Bluetooth Headset on Chromium or Firefox under Ubuntu, the audio cut out several times during video playback.  I also installed JetBrains Rider and attempted to compile and run unit tests for my open source project, Compare .NET Objects.  I would consider that project a tiny one.  The  CPU pegged at 100% while building the project and then it crashed Rider.  Sad.  

So I went back to Raspberry PI OS for the Raspberry PI.  I was able to watch YouTube videos without the audio cutting out.  I did not try to compile my open source project again.   In my opinion, the Raspberry PI OS doesn't look very good out of the box.  The whole reason why I wanted Ubuntu to work was that it looked so much better.  

Some Other Observations

  • Brave Browser does not work.  It has some crazy graphical glitches.
  • The Print Screen button does not work for Screen Shots
  • Libre Office Draw does not work.  All the icons are missing.
  • Beyond Compare does not work with Arm but it does work with AMD or X86

You can remote control VS Code on the PI.  I did not do that.  https://www.youtube.com/watch?v=Cs-lfskyjt0

With the CPU being atrocious on performance for compiling, I am basically done with Raspberry PI.  So I am off to testing Linux in x86 land.  Tune in for the next exciting episode.  Same bat channel, same bat time.

Reference Videos

Raspberry Pi 5 Operating Systems
https://www.youtube.com/watch?v=7G6jZd4gHtM

I replaced my PC with a Raspberry Pi 5. Here's how it went.

Raspberry Pi OS 2025.11.24 Review: Incredible Performance Boost & New Features! #raspberrypi #linux
https://www.youtube.com/watch?v=U3d1fXYGM1o

5 Best Operating Systems for Raspberry Pi 5

The BEST Operating System for Raspberry Pi 5? 

Friday, January 30, 2026

Learning Linux Part 2: Why Linux Users Say It Is Better Than Windows

I have spent many hours researching and watching videos why Linux users are using Linux instead of Windows.  I am just getting started so I don't know if any of this is true but here we go.


Advantages of Linux

  • Open Source - Free.  
  • Not forced to get new hardware as is the case for some Windows 10 users being forced to upgrade to Windows 11.
  • Performance
    • The GUI is responsive.
    • No heavy background telemetry or forced services.
    • Does not slow down over time. On Windows, older hardware slows down noticeably over time.
    • Lower system overhead.  Linux can run on older hardware.
  • Stability
    • The Kernel of the operating system is stable.  There are no blue screens like on Windows.
    • LTS (Long Term Support) versions are stable and are supported for years.
    • Servers can run for years without rebooting.
  • User Interface Customization
    • There is a choice of desktop environments including: GNOME, KDE, Xfce, Budgy, LXDE, LXQT, MATE, and Cinnamon.  
    • You can have a Windows 11 style layout, Windows 10 style layout, Windows 7 style layout, MacOS layout and more.
    • You can customize icons, fonts, and more.
  • Privacy
    • No tracking.  Even if you turn off tracking in Windows, it still collects some data.
  • Security
    • By default, users cannot make system changes.
    • Less malware is targeted toward Linux
  • Full control over updates.  You are no longer forced to update your operating system at an inconvenient time.  You can update automatically or manually.  You can block certain things from being updated.
  • Application package management is built in like an App Store.
  • You can boot the operating system from a USB Flash Drive or External Hard Drive
  • No Vendor Lock-In
    • No forced accounts, subscriptions, or cloud dependency.
    • You own the system, not the other way around.
  • No Advertisements

Disadvantages of Linux

  • Less applications than Windows.  
    • Adobe Creative Suite, AutoCAD are simply not available for Linux.
    • While most steam games work.  Some games do not work:  League of Legends, Valorant, Call of Duty, Battlefield, Destiny 2, Fortnite.  See:  https://www.protondb.com/ for compatibility.  Also see:  https://areweanticheatyet.com/
  • Hardware Incompatibility
    • Some hardware is not compatible: mice, printers, scanners, webcams, high end gaming peripherals.
    • You will need to research Linux compatibility first.
  • Learning Curve
    • Learning something new
    • Fear of installation woes
    • Terminal use is often necessary.
    • Troubleshooting may involve logs, configs, and forums.
    • Not as "click-everything" friendly for beginners.
  • Smaller Commercial Support
    • Less official vendor support compared to Windows in business settings.
    • Great community help.

Desktop Market Share

Linux is growing in popularity.  But why does it only have 4% share if it is supposedly better?  
  • Lack of public exposure.
  • Lack of applications.  Less commercially viable to produce applications.
  • Fear of something new
  • Fear of the command line
Here is the current market share:


Video Resources

I watched all these videos so you didn't have to.

Top 7 Things Linux Does Better than Windows


I Tried Switching to Linux… Again
https://www.youtube.com/watch?v=NqniwVaSJVA

I Switched to Linux for 30 Days

5 Reasons Linux Makes Your PC Awesome

Why Linux is Better Than Windows 11

Five Things Linux Does Better Than Windows

Linux Just KILLED Windows: "All Reasons Proven in 5 Minutes"

Should You Switch To Linux?

Why Linux users are Laughing at Windows Right Now : Covered in 4 Minutes

I’ve Had Enough of Windows - Switching to Linux

10 Things Linux Users Never Say #Shorts

Linux Will Work For You. Time to Dump Windows 10. And Don't Bother with Windows 11

I Used Linux for 8 Years: Here's What They Don't Tell You

Things To Know Before Switching To Linux

5 Reasons to FORGET Linux Today

Tuesday, January 27, 2026

Learning Linux Part 1: Building a Linux Machine

So begins my journey of learning Linux. I started with computers with a Commodore 64. In my first job in Columbus, Ohio, I used a Honeywell Level 62 mainframe. 



I have always had a fascination with small computers after using that monster of mainframe. I decided this year to learn Linux by using a Raspberry Pi 5.



I started out with an 8GB Raspberry PI since I thought I was most likely going to use Ubuntu.  The desktop version of Unbuntu needs at least 4GB of RAM.  When researching the Raspberry PI 5, I noticed several cases available.  I was looking for a case that supported NVME drives.  I settled on the Pironman 5 since it not only supported NVME but also supported two full size HDMI ports, and it had RGB lights! 



Here is what I ordered:

Raspberry PI 5

https://www.amazon.com/CanaKit-Raspberry-Basic-Kit-Card/dp/B0CVR1LP7G

Pironman 5

https://www.amazon.com/Pironman-Raspberry-Shutdown-Standard-Performance/dp/B0D5CTRSQK

NVME

https://www.amazon.com/SAMSUNG-Internal-Expansion-MZ-V9P1T0B-AM/dp/B0BHJF2VRN

NVME Enclosure

https://www.amazon.com/dp/B09T97Z7DM

Micro SD Card for the initial boot

https://www.amazon.com/SanDisk-Ultra-microSDXC-Memory-Adapter/dp/B0B7NXBM6P

I know what you are thinking.  Wow that is a lot of money for a computer that is less powerful than an old laptop.  True, but half the fun of it is putting the thing together.

I had some trouble with getting it working namely:

  • The SD Card extender was faulty.  I had to request a replacement (which was free). 
  • I accidentally did not seat the NVME cable all the way in and initially my drive did not work.

Here is the main video that I followed for the setup:

Pironman 5 Setup: Raspberry Pi 5 Case with NVMe M.2 SSD - Mini Pi PC

https://www.youtube.com/watch?v=Rx9UNRfzjI4

I also heavily used this support page from SunFounder:

https://docs.sunfounder.com/projects/pironman5/en/latest/pironman5/intro_pironman5.html


Friday, October 3, 2025

Creating Integration Tests for Databricks

Integration Tests with Databricks

Overview

  • There are no special tools required for testing Databricks Notebooks. All that is required is Python, Pytest, and patience.

Strategy

  • Use mock data.
  • Use the Arrange, Act Assert pattern
  • Leave the system as you found it. Perform a data cleanup.
  • Run the full suite of integration tests when deploying to an environment.

Create a New Branch

Before you do anything else, create a new branch off your development branch. Example: main or develop

Open with VS Code

Checkout the branch and open the directory with VS Code.

Create Folders

Integration Tests should be separated from Unit Tests. In VS Code, in your project, create a folder structure like this:

📁 project
    📁 tests
        📁 integration
        📁 unit

Create example tests

Create an example passing test in the integration and unit folders. Name the file:

test_example.py

'''
This is an example unit test
'''
import logging
import pytest

logger = logging.getLogger()

# test data
MESSAGE = "Hello World"

def test_pass():
    '''Example pass scenario'''
    logger.info("This is an example unit test for a pass scenario")
    assert MESSAGE == "Hello World"

Create a test runner

A Databricks Python notebook must be created to run the test. It has to be a Python notebook so that any notebooks that are run from it will have debugging information included.

pytest_databricks.py

# Databricks notebook source
# MAGIC %pip install pytest
# MAGIC import pytest
# MAGIC import sys
# MAGIC
# MAGIC sys.dont_write_bytecode = True
# MAGIC
# MAGIC retcode = pytest.main([".", "-v", "-p", "no:cacheprovider"])
# MAGIC assert retcode == 0, "The pytest invocation failed. See the log for details."

Verify the Example Test

  1. Check in your branch into Git
  2. Launch Databricks
  3. Run the pytest_databricks.py for your project in Databricks to ensure it works.

Plan the Integration Test for your Databricks Notebook

  • Identify the parameters and inputs required for the Databricks Notebook.
  • Identify what is output by the Databricks Notebook.
  • Decide what is critical to verify: table existence, column presence, expected data, files.
  • Identify cleanup steps: remove tables, delete directories, files, etc.

Create the Integration Test

Use the template below to create the integration test. The file and method must begin with test_.

Example Databricks Integration Test

import sys
import os
import shutil
import logging
import pytest
from common import TestUtil
from pyspark.sql import SparkSession
from pyspark.dbutils import DBUtils

logger = logging.getLogger()

spark = SparkSession.getActiveSession()
if spark is None:
    spark = SparkSession.builder.getOrCreate()

dbutils = DBUtils(spark)

integration_test_volume = TestUtil.get_integration_test_volume()   
integration_test_table_path = TestUtil.get_integration_test_table_path()
catalog_name = integration_test_table_path.split('.')[0]
schema_name = integration_test_table_path.split('.')[1]    
source_dir = f"{integration_test_volume}/source_dir"
dest_dir = f"{integration_test_volume}/dest_dir"
output_table = f"{integration_test_table_path}.output_table"

def _cleanup():    
    TestUtil.recreate_directory(source_dir)
    TestUtil.recreate_directory(dest_dir)
    TestUtil.drop_table_if_exists(output_table)
    logger.info("✅ Cleanup Completed")
    
def test_my_notebook():
    _cleanup()
    
    # Arrange
    logger.info(f"✅ Test data created")
    notebook_path = "../../notebooks/notebook_to_test"  

    params = {
        "parameter_1": "1",
        "parameter_2": "2",
        "parameter_3": "3"
    }

    # Act
    result = dbutils.notebook.run(
        notebook_path,
        timeout_seconds=300,
        arguments=params
    )

    logger.info("✅ Notebook finished")

    # Assert
    TestUtil.verify_table_exists(output_table)

    expected_cols = [
        "Column1",
        "Column2"
    ]

    TestUtil.verify_table_columns(output_table, expected_cols)
    TestUtil.verify_column_data(output_table, "Column1", "Hello World")
    logger.info("✅ Assert Completed")

    _cleanup()
    logger.info("✅ Cleanup Completed")

Test Util

This class has common helper methods:

class TestUtil:

    @staticmethod
    def recreate_directory(path: str) -> None:
        try:
            dbutils.fs.rm(path, recurse=True)
            logger.info(f"Deleted: {path}")
        except Exception as e:
            logger.info(f"(Info) Could not delete {path}, may not exist yet: {e}")
        dbutils.fs.mkdirs(path)
        logger.info(f"✅ Recreated: {path}")

    @staticmethod
    def verify_table_columns(table_full_name: str, expected_columns: list[str]) -> None:
        if not spark.catalog.tableExists(table_full_name):
            assert False, f"❌ Table does not exist: {table_full_name}"
        df = spark.table(table_full_name)
        table_columns = [c.lower() for c in df.columns]
        missing = [col for col in expected_columns if col.lower() not in table_columns]
        if missing:
            assert False, f"❌ Missing columns in {table_full_name}: {', '.join(missing)}"
        logger.info(f"✅ All columns exist in {table_full_name}")

    @staticmethod
    def verify_column_data(table_name: str, col_name: str, data: any) -> None:
        df = spark.table(table_name)
        matching = df.where(df[col_name] == data).limit(1).count()
        assert matching > 0, f"❌ No record found with {col_name} = {data} in {table_name}"

    @staticmethod
    def verify_table_exists(table_name: str) -> None:
        if not spark.catalog.tableExists(table_name):
            assert False, f"❌ Table does not exist: {table_name}"
        logger.info(f"✅ Verified table exists: {table_name}")

    @staticmethod
    def drop_table_if_exists(table_name: str) -> None:
        if spark.catalog.tableExists(table_name):
            spark.sql(f"DROP TABLE {table_name}")
            logger.info(f"✅ Dropped table: {table_name}")

    @staticmethod
    def get_integration_test_volume() -> str:
        return os.environ.get("INTEGRATION_TEST_VOLUME", "/Volumes/test_volume")

    @staticmethod
    def get_integration_test_table_path() -> str:
        return os.environ.get("INTEGRATION_TEST_TABLE_PATH", "test_catalog.test_schema")

Thursday, October 2, 2025

Blazor Disconnects When Uploading a File

The InputFile control in Blazor is finicky.  The entire application shuts down locally after attempting to upload a file.  When deployed, the session resets.  What are some reasons why this happens?

1.  There is a breakpoint set your file upload method.  Believe it or not, the application will completely shut down if you just have a breakpoint set when running locally.

2.  The file is being read into memory and then performing some processing on it and then saving it to a file.  The InputFile expects to immediately save the file to disk without any waiting.

3.  The max file size is less than the size of the file.

4.  The Blazor signal R receive size is less than the size of the file.

I had a requirement to remove the Geolocation from large images, resize it to a maximum of 1000 pixels width or height, and convert to webp.  All of these things came into consideration.

In your program.cs ensure you have set the MaximumReceiveMessageSize to the largest file you will have.  In the example below it is 300MB

builder.Services.AddSignalR(o => { o.MaximumReceiveMessageSize = 300 * 1024 * 1024; });

Here is how to define an inputfile

<InputFile id="fileUpload" hidden OnChange="HandleFileUpload" multiple accept="@String.Join(',',AllowedExtensions)" />

This is the code behind for handling the upload.

public int MaxFileSize { get; set; } = 300 * 1024 * 1024;
public string CurrentFolderPath { get; set; }
public List<string> AllowedExtensions { get; set; } = new List<string>()
{
".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"
};
private async Task HandleFileUpload(InputFileChangeEventArgs e)
{
try
{
if (e == null) return;
IReadOnlyList<IBrowserFile> inputFiles = e.GetMultipleFiles();
foreach (var file in inputFiles)
{
if (!await HandleSingleFile(file)) return;
}
//Do your other processing here after the files have been saved
foreach (var file in inputFiles)
{
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to upload files to {CurrentFolderPath}", CurrentFolderPath);
ErrorMessage = $"Failed to upload: {ex.Message}";
}
finally
{
StateHasChanged();
}
}
private async Task<bool> HandleSingleFile(IBrowserFile file)
{
var targetPath = Path.Combine(CurrentFolderPath, file.Name);
if (!AllowedExtensions.Contains(Path.GetExtension(file.Name)))
{
ErrorMessage = $"File type not allowed for {file.Name}. Valid File Types are:<br />" +
String.Join("<br />", AllowedExtensions);
return false;
}
if (file.Size > MaxFileSize)
{
ErrorMessage = $"File size exceeds the maximum limit of {MaxFileSize / 1024.0 / 1024.0:F2} MB for for {file.Name}.");
return false;
}
using (var targetStream = new FileStream(targetPath, FileMode.Create))
{
using (var fileStream = file.OpenReadStream(MaxFileSize))
{
await fileStream.CopyToAsync(targetStream);
}
}
return true;
}

See the full implementation of uploading a file, removing the GeoLocation, resizing and converting to .webp here:

Thursday, June 26, 2025

Blazor Server Website does not work with AT&T Mobile Data

Stack Overflow did not like my question so I am posting it here.

I have created a Blazor Server Website using .NET 8.

Forms and the localization do not work with AT&T Mobile Data. The site works with Verizon, T-Mobile, and WiFi. Has anyone else built a Blazor Server application that works on AT&T mobile data?

Here is the site: http://gregfinzer2-001-site3.ctempurl.com/

Here is the code: https://github.com/GregFinzer/BedBrigadeNational

To reproduce the issue, on the home page change from English to Spanish. On AT&T Mobile data it does not change to Spanish. On everything else, it works.

Here is what fails:

  • Samsung S25+ with Chrome, Brave, Firefox, and Edge on AT&T Mobile Data fails
  • Samsung S25 with Chrome on AT&T Mobile Data fails
  • Samsung S21 on AT&T Mobile Data fails with Chrome (in Puerto Rico)
  • Motorola G using Chrome with AT&T Mobile Data Hotspot Fails
  • Samsung Tab A7 Lite on Chrome and AT&T Mobile Data Hotspot Fails
  • Laptop on AT&T Mobile Data Hotspot Fails

These works:

  • Desktop on WiFi
  • Android on WiFi
  • iPhone 16 Pro on WiFi
  • Google Pixel 5 with Verizon Mobile Data
  • Samsung S21 on T-Mobile Mobile Data

I have tried clearing the cache. The Motorola G was a brand new phone that I bought for testing and that did not work the first time. I have also tried the Chrome Inspect with the phone connected to my desktop PC. The inspect does not load at all. I also tried installing the DevTools for Firefox on Android. There are no console errors.

Another test is that if you click Bed Brigade near me, the search form is just completely missing with Android and AT&T mobile data. Anything else you will see a search form. Type in 43228 for the zip code to see the result.

New Information

I connected my laptop to the AT&T mobile data hotspot and now I am actually able to see an error in the Chrome developer tool console when the website loads: Uncaught (in promise) SyntaxError: Unexpected token '�', "�" is not valid JSON

I went through several different attempts at fixes with ChatGPT including changing it to only use WebSockets, attempting to use a _Host.cshtml in Blazor 8, and doing long polling.

Here is the conversation with ChatGPT: https://chatgpt.com/share/6851886e-1fb0-8005-a475-421c708d2a56

Here are the PRs with the attempted changes:


Here is the PR: