Tuesday, October 21, 2014

A JUnit XSL Transform For PHPUnit Logging

I recently worked on a PHP application with a PHPUnit test suite. Not finding a good way to view the test run results, I settled on logging the output into JUnit format and then applied the style sheet to the XML file so that I can through the XML file into a browser.
time phpunit --testsuite "Application Test Suite" --log-junit public/testresults.xml & sed -i 's//\n/g' public/testresults.xml
Not sure why, but finding a decent transform for the JUnit output was more diffcult than expected. I decided to extended an XSL transform in this StackOverflow answer. It is a pretty good start, plain text, but I can work with it.

After applying Bootstrap styling, the end result is a pleasantly styled JUnit view in a browser. For some reason, FireFox was not applying the bootstrap CSS file, so I just brought in the styles that I needed, thus the style block.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/testsuites">
        <!-- <xsl:text disable-output-escaping='yes'>&lt;!DOCTYPE html></xsl:text> -->
        <html class="no-js" lang="en">
        <head>
            <meta charset="utf-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <title>PHPUnit Test Results</title>
            <meta name="description" content="" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <!-- <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" /> -->
            <style>
                html {
                  font-family: sans-serif;
                  -webkit-text-size-adjust: 100%;
                      -ms-text-size-adjust: 100%;
                  font-size: 10px;
                  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
                }
                body {
                  margin: 0;
                  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
                  font-size: 14px;
                  line-height: 1.42857143;
                  color: #333;
                  background-color: #fff;
                }
                article,
                aside,
                details,
                figcaption,
                figure,
                footer,
                header,
                hgroup,
                main,
                nav,
                section,
                summary {
                  display: block;
                }
                hr {
                  height: 0;
                  -webkit-box-sizing: content-box;
                     -moz-box-sizing: content-box;
                          box-sizing: content-box;
                }
                pre {
                  overflow: auto;
                  font-family: monospace, monospace;
                  font-size: 1em;
                }

                .container {
                  padding-right: 15px;
                  padding-left: 15px;
                  margin-right: auto;
                  margin-left: auto;
                }
                .page-header {
                    padding-bottom: 9px;
                    margin: 40px 0 20px;
                    border-bottom: 1px solid #eee;
                }
                .failure, .error {
                    padding: 0px 20px;
                }
                .failure pre, .error pre {
                    border: 1px solid #DDD;
                    padding: 10px;
                }
            </style>
        </head>
        <body>
            <div class="container">
                <xsl:apply-templates select="testsuite" />
            </div>
        </body>
        </html>
    </xsl:template>

    <xsl:template match="testsuite">
        <header class="page-header">
        <h1>Testsuite: <xsl:value-of select="@name" /></h1>
        <div>
            <xsl:text>
            Tests run: </xsl:text>
            <xsl:value-of select="@tests" />
            <xsl:text>, Failures: </xsl:text>
            <xsl:value-of select="@failures" />
            <xsl:text>, Errors: </xsl:text>
            <xsl:value-of select="@errors" />
            <xsl:text>, Time elapsed: </xsl:text>
            <xsl:value-of select="@time" />
            <xsl:text> sec</xsl:text>
        </div>
        </header>
        <xsl:apply-templates select="system-out" />
        <xsl:apply-templates select="system-err" />
        <div>
            <xsl:apply-templates select="//testcase" />
        </div>
    </xsl:template>

    <xsl:template match="testcase">
        <p>
            <xsl:text>
            Testcase: </xsl:text>
            <xsl:value-of select="@name" />
            <xsl:text> took </xsl:text>
            <xsl:value-of select="@time" />
        </p>
        <xsl:apply-templates select="failure" />
        <xsl:apply-templates select="error" />
    </xsl:template>

    <xsl:template match="failure">
        <div class="failure">
            <span style="color: #ff4136;">
                <xsl:text>
                    Failure:
                </xsl:text>
                <xsl:value-of select="@type" />
            </span>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="error">
        <div class="error">
            <span style="color: #F00;">
                <xsl:text>
                    Error:
                </xsl:text>
                <xsl:value-of select="@type" />
            </span>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="system-out">
        <div>
            <xsl:text>
            ------ Standard output ------
            </xsl:text>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="system-err">
        <div>
            <xsl:text>
            ------ Error output ------
            </xsl:text>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

</xsl:stylesheet>

Tuesday, October 7, 2014

Xamarin Forms Custom Controls Without Native Renderers

My last blog post discussed how to create reusable custom content views. It involved leveraging the ContentPage element's ViewModel. This is great when you want to easily pull common code out and make it reusable. This will be a great place to start building.

As with my last blog post, I was not able to find examples of people doing this pattern. It seems like such a common thing to want to do. I digress.

Let's say that there is a repeated layout structure which only differs by the ViewModel properties. What about making a custom control which do not depend on a ViewModel?

A custom control which doesn't depend on a ViewModel internally, will need to have properties which we can bind to a ContentPage ViewModel's property. We want to end up with something like:
<localcontrol:CountLabelView
    CountText="{Binding path=MessageCount}"
    Text="{Binding path=MessageText}" />
The first task is to add a new Forms Xaml Page to the project. I'll call it "CountLabelView".

Then, my experience with building native controls comes in handy. We can add BindableProperty fields to our custom control's code behind. One for each property we want to have available to our control.
public static readonly BindableProperty TextProperty =
    BindableProperty.Create<CountLabelView, string>(
        p => p.Text, 
        "", 
        BindingMode.TwoWay, 
        null,
        new BindableProperty.BindingPropertyChangedDelegate<string>(TextChanged), 
        null, 
        null);

public string Text
{
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value); }
}

static void TextChanged(
    BindableObject obj, 
    string oldPlaceHolderValue, 
    string newPlaceHolderValue)
{
    
}
Now we have a way for a ContentPage to bind properties to custom ContentView controls.

Next we can move our layout into the ContentView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentView 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:IntPonApp;assembly=IntPonApp"
    xmlns:custom="clr-namespace:CustomControls.Controls;assembly=CustomControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    x:Class="IntPonApp.Controls.CountLabelView">
 
 <StackLayout 
        Orientation="Horizontal" 
        HorizontalOptions="FillAndExpand" 
        VerticalOptions="FillAndExpand" 
        Padding="0">
  <custom:RoundFrame 
            HorizontalOptions="CenterAndExpand" 
            VerticalOptions="CenterAndExpand" 
            Padding="7,1" 
            BorderRadius="40" 
            FillColor="#333333" 
            HasShadow="false">

   <Label    x:Name="CountTextLabel"
                Text="{Binding CountText}" 
                VerticalOptions="Center" 
                XAlign="Center" 
                TextColor="#FFFFFF" />

  </custom:RoundFrame>

  <StackLayout 
            Spacing="0" 
            HorizontalOptions="StartAndExpand" 
            VerticalOptions="CenterAndExpand" 
            Padding="0">

   <Label    x:Name="TextLabel"
                Text="{Binding Text}" 
                LineBreakMode="WordWrap" 
                XAlign="Center" 
                HorizontalOptions="StartAndExpand" 
                VerticalOptions="Center" 
                TextColor="#DEDEDE" />

  </StackLayout>
 </StackLayout>
 
</ContentView>
This is great except, if you run this, the application will fail (unless CountText and Text happen to exist in your ContentPage's ViewModel. This is problematic, but not an insurmountable obstacle. We may have a couple options:
  1. We can remove the binding from the elements, assign names to the elements which need binding, hook up onchange events (not useful in this case because we are using labels and not Entry elements) which would assign the value back to the BindableProperty, and then find the named elements and assign the value from the BindableProperty.
  2. We can assign names to the elements which need binding and then find the named elements and assign the BindingContext to the custom control object.
  3. We can assign the custom control's BindingContext to the custom control itself.
Option 1 would be a mess to implement and maintain. It has to be replicated for every control which needs binding and there is a good chance that the bindings won't work as one should expect.

Option 2: We are able to bind in a much more expected fashion and changes will cascade as we expect. However this will still result in extra 1 line of code per control needing to be bound.

Option 3: It gets rid of the inherited BindingContext and forces the control to be self sufficient and thus reusable regardless of the ViewModel. Plus, the bindings work as one expects and it is only ONE line of code.

In testing Option 3, I found that the BindingContext was not being inherited correctly to the child controls. I suspect this is a bug in Xamarin. So, at the moment Option 2 is the best option that works.

Below is the code behind for the custom control.
public partial class CountLabelView
{
    #region Properties

    public static readonly BindableProperty TextProperty =
        BindableProperty.Create<CountLabelView, string>(
            p => p.Text, 
            "", 
            BindingMode.TwoWay, 
            null,
            new BindableProperty.BindingPropertyChangedDelegate<string>(TextChanged), 
            null, 
            null);

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    static void TextChanged(
        BindableObject obj, 
        string oldPlaceHolderValue, 
        string newPlaceHolderValue)
    {
        
    }


    public static readonly BindableProperty CountTextProperty =
        BindableProperty.Create<CountLabelView, string>(
            p => p.CountText, 
            "", 
            BindingMode.TwoWay, 
            null,
            new BindableProperty.BindingPropertyChangedDelegate<string>(CountTextChanged), 
            null, 
            null);

    public string CountText
    {
        get { return (string)GetValue(CountTextProperty); }
        set { SetValue(CountTextProperty, value); }
    }

    static void CountTextChanged(
        BindableObject obj, 
        string oldPlaceHolderValue, 
        string newPlaceHolderValue)
    {

    }

    #endregion Properties

    #region Constructor

    public CountLabelView()
    {
        InitializeComponent();
        CountText = "1";
        //this.BindingContext = this;
        CountTextLabel.BindingContext = this;
        Text1Label.BindingContext = this;
        Text2Label.BindingContext = this;
    }

    #endregion Constructor
}
A point of note, the constructor contains this line CountText = "1";. This is required because there is a custom native control surrounding the label with a binding. For some reason the rendering process executes initially before the binding finishes and not having an initial value on the control will result in the custom native control's height to be ~1px. I suspect that this is another bug in Xamarin.

Then you add the XML namespace to the ContentPage declaration and add the custom control.
xmlns:localcontrol="clr-namespace:IntPonApp.Controls;assembly=IntPonApp"
<localcontrol:CountLabelView
    CountText="{Binding path=MessageCount}"
    Text="{Binding path=MessageText}" />

Wednesday, October 1, 2014

Xamarin Forms Reusable Custom Content Views

There is a lot (relatively) of information on how to make reusable native controls with renderers and native views. However there is next to nothing about how to make reusable controls which don't require renderers or native views.

[Edit 2014-12-05] The project I developed required the same header on each page, so I figured I would use that as a way to describe how to create resuable controls. Performance-wise, it shouldn't be any worse than loading the information on every page. I needed the NavigationPage functionality, but without the navigation bar. It is probably not ideal, but it is what the design imposed.

Let's assume that we have the following XAML which needs to be replicated on several pages.
<Grid x:Name="SharedHeaderBar" HorizontalOptions="FillAndExpand" Padding="5,10,5,-5" BackgroundColor="#3FB5C1" >
    <Grid.RowDefinitions>
      <RowDefinition Height="45" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Image x:Name="homeImageiOS" Source="{local:ImageResource IntPonApp.Resources.IntPon-Logo.png}" Grid.Row="0" Grid.Column="0" />
    <Button x:Name="logoffButtoniOS" Text="{Binding Path=FullName}" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="2" />
  </Grid>
My first foray into this was unsuccessful because I tried using a View. I stumbled upon the ContentView. I couldn't find any examples of people using it. Having had a lot of experience with WPF and knowing what I wanted to achieve, this seemed to be the most promising path.

I created a new Forms Xaml Page and changed the ContentPage tags to ContentView. Then I moved the reused XAML into the ContentView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:IntPonApp;assembly=IntPonApp"
        x:Class="IntPonApp.Controls.SharedHeaderView">
  
  <Grid x:Name="SharedHeaderBar" HorizontalOptions="FillAndExpand" Padding="5,10,5,-5" BackgroundColor="#3FB5C1" >
    <Grid.RowDefinitions>
      <RowDefinition Height="45" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Image x:Name="homeImageiOS" Source="{local:ImageResource IntPonApp.Resources.IntPon-Logo.png}" Grid.Row="0" Grid.Column="0" />
    <Button x:Name="logoffButtoniOS" Text="{Binding Path=FullName}" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="2" />
  </Grid>

</ContentView>
It is important to understand the state of the binding context. The binding context will be inherited from the parent control where it is used. This is very convenient because I wanted to pull a value from the ContentPage's ViewModel.

I pulled over the event bindings and the needed navigation functions. Part of the functionality that I wanted to move into the ContentView needed to raise a DisplayAlert which, as it turns out, isn't available on the ContentView element. So I flexed my recursive skills and created the following to get the ContentPage element.
public ContentPage FindParentPage(Element el = null)
{
    if (el == null)
        el = this;
    return 
          (el is ContentPage) ? (ContentPage)el
        : (el.Parent != null) ? FindParentPage(el.Parent) 
        : null;
}
Then I was able to display alert messages.

Below is the resulting code behind.
public partial class SharedHeaderView
{
    public SharedHeaderView()
    {
        InitializeComponent();

        string platformName = Device.OS.ToString();

        this.FindByName<Button>("logoffButton" + platformName)
            .Clicked += OnLogoffClicked;
        this.FindByName<Image>("homeImage" + platformName)
            .GestureRecognizers.Add(new TapGestureRecognizer((view, args) =>
        {
            this.Navigation.PopToRootAsync();
        }));
    }

    protected async void OnLogoffClicked(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(App.ApiKey))
        {
            string errorMessage = "";
            try
            {
                if (await FindParentPage()
                    .DisplayAlert("Sign Off", "Are you sure?", "Yes", "No"))
                {
                    App.Logout();
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
            }

            if (!string.IsNullOrEmpty(errorMessage))
            {
                await FindParentPage()
                    .DisplayAlert("Help", errorMessage, "OK");
            }
        }
    }

    public ContentPage FindParentPage(Element el = null)
    {
        if (el == null)
            el = this;
        return 
              (el is ContentPage) ? (ContentPage)el
            : (el.Parent != null) ? FindParentPage(el.Parent) 
            : null;
    }
}
Then you add the XML namespace to the ContentPage declaration and add the XML node.
xmlns:localcontrol="clr-namespace:IntPonApp.Controls;assembly=IntPonApp"
<localcontrol:SharedHeaderView />
Getting this to work has really saved me a lot of unneeded duplication.

Edit 2014-12-05: Edited to add comment/response/excuse to a question on StackOverflow: Is it possible to create a custom page layout with Xamarin.Forms?