Wednesday, March 26, 2025

Tests in the very same console app - compilation error in .NET Core

Having unit tests in the very same console app causes a compilation error:

Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point.

Kind of unexpected, there's definitely a single Main.

There problem seems to be there for all these years, despite being first described back in 2017

The solution is alread provided in the link above, just add

<GenerateProgramFile>false</GenerateProgramFile>

to the *.csproj

A brave little cookie-footer

One of our apps contains a page and the page has a div. The div's class name is cookie-footer.

The div itself has nothing to do with actual cookies, it contains a content that user is supposed to see.

And what? Turns out Brave doesn't show that div. It just adds:

// user agent stylesheet
.cookie-footer {
    display: none !important;
}

What's bizzare, Brave doesn't add this always. We have the app on multiple domains, the user agent style is added on most of domains but not all of them!

Tried other variants:

  .cookie-footer  - blocked
  .cookiefooter   - blocked
  .cookie--footer - works
  .cookiee-footer - works
  .coookie-footer - works

Great times. It's not only the legal regulation that can block your content, it's also your browser heuristic.

Monday, March 17, 2025

A fairy tale of misusing the C# typesystem

Once upon a time in a kingdom far far away someone wrote a code that required two string arguments:

    public class Worker
    {
        public void DoWork( string name, string surname )
        {
            Console.WriteLine( $"{name} {surname}" );
        }
    }

All the people used the code for years without any issues:

    new Worker().DoWork( "john", "doe" );

Then, someone in a hurry did something bad which should never happen. Arguments were swapped in a call:

    new Worker().DoWork( "doe", "john" );

Consequences were severe.

The culprit was tried and expelled from the kingdom. The king called for his best wizards and asked them to do something so that it never ever happens in the future.

One of the wizards suggested that introducing types would make it clear of what real intentions of arguments are:

    public class Name
    {
        public Name( string value )
        {
            this.Value = value;
        }

        public string Value { get; set; }

        public override string ToString()
        {
            return this.Value;
        }
    }

    public class Surname
    {
        public Surname( string value )
        {
            this.Value = value;
        }

        public string Value { get; set; }
        public override string ToString()
        {
            return this.Value;
        }
    }


    public class Worker
    {
        public void DoWork( Name name, Surname surname )
        {
            Console.WriteLine( $"{name} {surname}" );
        }
    }

Initially people complained a bit but then started to get used to the new calling convention:

    new Worker().DoWork( new Name( "john" ), new Surname( "doe" ) );

The problems were gone. Everyone was happy.

Years passed, some wizards were gone, new wizards came to the kingdom. One of new wizards reviewed the code and came up with an idea.

- Why this convention is that awkward, why wrap strings in auxiliary types? - thought the wizard.

And he came up with an idea to add implicit conversion operators:

    public class Name
    {
        public Name( string value )
        {
            this.Value = value;
        }

        public string Value { get; set; }

        public override string ToString()
        {
            return this.Value;
        }

        public static implicit operator Name( string value )
        {
            return new Name( value );
        }
    }

    public class Surname
    {
        public Surname( string value )
        {
            this.Value = value;
        }

        public string Value { get; set; }
        public override string ToString()
        {
            return this.Value;
        }

        public static implicit operator Surname( string value )
        {
            return new Surname( value );
        }
    }
 

The new wizard was very proud of himself. He barely told anyone of his conversion operators so everyone else was still using the well established convention:

   new Worker().DoWork( new Name( "john" ), new Surname( "doe" ) );

But, since the conversion was now implicit, the wizard was able to make his own code shorter:

   new Worker().DoWork( "john", "doe" );

Years passed, new people arrived and then, someone in a hurry did something bad which should never happen. Arguments were swapped in a call:

    new Worker().DoWork( "doe", "john" );

Consequences were severe.

Was the culprit tried and expelled from the kingdom, same as last time?

Not really, the Wizard Council blamed the new wizard, the one who introduced both implicit conversion operators.

He was tried and expelled from the kingdom. His changes were reverted forever and everyone lived happily ever after.


This is based on a (almost) true story.

But can it run DOOM?

I barely repost news from elsewhere but this time this is extremely impressive.

It was announced that Dimitri Mitropoulos from the TypeScript team was able to build a WebAssembly interpreter in the TypeScript typesystem and then make it run DOOM, still inside the type system. Well, not quite "run", just render the first frame which took 12 days.

Anyway, it's inspiring to hear such news. Congratulations to the team!

Sunday, March 16, 2025

XNADash ported to .NET8/Monogame

Years ago, in 2011, I've blogged about an old DOS game I've ported to XNA. This weekend I've found the code and spent a while to make sure it works.

The new version targets .NET8/Monogame and is available on my Github.

Monday, March 10, 2025

OldMusicBox.ePUAP.Client 1.25.3

Bumped the OldMusicBox.ePUAP.Client to 1.25.3.

For some unknown reason that doesn't seem to be announced, the TpSiging5::GetSignedDocument changed the format of natural person's data (givenname, surname, personal identification number).

From the very beginning of the old TpSigning service, the document contained user signatures in the PodpisZP node. Later on, when TpSigning5 was introduced, they changed the signature to EPSignature node (different node with different model).

And guess what, starting from somewhere between 07-03-2025 and 10-03-2025, the new service (TpSigning5) returns the natural person's data in the old format (back to PodpisZP). Be aware of this and update your code accordingly.