Pokazywanie postów oznaczonych etykietą programming. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą programming. Pokaż wszystkie posty

poniedziałek, 5 października 2015

Walidacja bibliotek jako krok w CI

Skąd wież, że masz poprawne informacje w biblioteczce (.dll)?
Skąd wież, że wszystkie biblioteki w projekcie mają taką samą wersję lub nazwę produktu?
Ja napisałem mały skrypcik do walidacji biblioteczek pod względem wersji. Poniżej jest skrypt:
function Test-AssemblyVersion
{
    param(
        [Parameter(
        Position=0, 
        Mandatory=$true, 
        ValueFromPipeline=$true,
        ValueFromPipelineByPropertyName=$true)
    ]
    [String]
    $filePath
    ,
    [Parameter(
        ValueFromPipeline=$false)
    ]
    [String]
    $ProductVersion
    ,
    [Parameter(
        ValueFromPipeline=$false)
    ]
    [String]
    $FileVersion
    )
    begin{
        $invIC= [System.StringComparison]::InvariantCultureIgnoreCase
    }
    process {
        $file = $_
        if(test-path $file)
        {
            $ass = [System.Reflection.Assembly]::LoadFile($file)
            $fvi = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($ass.Location)
            if( $ProductVersion.Equals($fvi.ProductVersion, $invIC) 
               -and  $FileVersion.Equals($fvi.FileVersion, $invIC) )
            {
                Write-Host "$file is ok."
            }
            else
            {
                throw "Wrong ProductVersion or  FileVersion in $file."
            }
        }
    }
}
Przykładowe wywołanie przedstawione jest poniżej:
gci -path .\MyProject\bin\ -recurse -filter 'MyProject.*.dll' | Test-AssemblyVersion -ProductVersion 2.1.0.0 -FileVersion 2.1.0.0
Można teraz dodać wywołanie i funkcję do jednego pliku i cały kod wyglądałby tak:
param(
[string]$ProductVersion, 
[string]$FileVersion
)
function Test-AssemblyVersion
{
    param(
        [Parameter(
        Position=0, 
        Mandatory=$true, 
        ValueFromPipeline=$true,
        ValueFromPipelineByPropertyName=$true)
    ]
    [ValidateNotNullOrEmpty()]
    [String]
    $filePath
    ,
    [Parameter(
        Position=1, 
        Mandatory=$true, 
        ValueFromPipeline=$false,
        ValueFromPipelineByPropertyName=$false)
    ]
    [ValidateNotNullOrEmpty()]
    [String]
    $ProductVersion
    ,
    [Parameter(
        Position=2, 
        Mandatory=$true, 
        ValueFromPipeline=$false,
        ValueFromPipelineByPropertyName=$false)
    ]
    [ValidateNotNullOrEmpty()]
    [String]
    $FileVersion
    )
    begin{
        $invIC= [System.StringComparison]::InvariantCultureIgnoreCase
    }
    process {
        $file = $_
        if(test-path $file)
        {
            $ass = [System.Reflection.Assembly]::LoadFile($file)
            $fvi = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($ass.Location)
            if( $ProductVersion.Equals($fvi.ProductVersion, $invIC) -and  $FileVersion.Equals($fvi.FileVersion, $invIC) )
            {
                Write-Host "$File is ok. ProductVersion is $($fvi.ProductVersion) and FileVersion is $($fvi.FileVersion)"
            }
            else
            {
                throw "Wrong ProductVersion or  FileVersion in $file. ProductVersion is $($fvi.ProductVersion) and FileVersion is $($fvi.FileVersion)"
            }
        }
    }
}
$root  = ( gi  '..\..'  ).FullName
Write-host "Root is $root"
$OutputPackage = "$root\bin"
if(Test-path $OutputPackage)
{
    $fcraLib  = gci -path $OutputPackager -filter 'MyProject.*.dll' 
    if( $fcraLib)
    {
        $fcraLib | %{$_.Fullname} | Test-AssemblyVersion -ProductVersion $ProductVersion -FileVersion $FileVersion
    }
    else
    {
        throw "No dll files in dir: $OutputPackage "
    }
}
Teraz można ten skrypt podpiąć do TeamCity.

czwartek, 20 listopada 2014

W końcu będzie null-conditional operator w C# 6

3 dni temu obejrzałem filmik z nowymi featuresami C#. Jest, w końcu, dziękuję. Wiem, że c# jest bardzo dynamicznie rozwijanym językiem, ale oczekiwałem ... może od kiedy zacząłem programować obiektowo, bezpiecznej nawiagacji po polach nie zważając na null'e. Odkąd ludzie wymyślili wartość nulla to nazwali tą wartość błędem wartym miliard dolarów. Już wcześniej operator bezpieczeń nawigacji był stosowany w innych językach. Słyszałem, że ma być null-conditional operator, ale myślałem, że ten operator promuje projekt Roslyn, więc nie wiedziałem czy ten operator będzie standardowo w C#.




A poniżej jest filmik z konferencji BUILD 2014:



A tutaj filmik z TechEd


Co prawda, w prezentacjach są te same przykłady kody, jakby nie mogli wymysleć innych zastosowań niż konwerter tablicy pointów do JSONa :)
Wydaje mi się, że w taki sposób próbują utrwalić wiedzę.

Jeszcze podoba mi się operator nameof. Wiem, że parę moich znajomych na ten operator czekało.

środa, 12 listopada 2014

Wyjście na notatnik czyli oczekiwany out-notepad w PS

Bawiąc się z PowerShellem, zauważyłem brak wysłania rezultatów do notatnika w stylu Out-GridView. Postanowiłem napisać skrypcik, który by to umożliwiał. Na samym początku jest potrzebna funkcja do znajdywania okna z aplikacją oraz wysyłania tekstu do niej. Skorzystamy z biblioteki user32.dll. Skrypt wyglada następująco:

$User32MethodDefinition = @'
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);    
'@

$User32 = Add-Type -MemberDefinition $User32MethodDefinition -Name 'user32' -Namespace 'Win32' -PassThru
Za każdym razem jak uruchomimy skrypt to będziemy musieli stworzyć nowy obiekt $user32 (i będziemy musieli wiedzieć czy obiekt ma wymagane metody). Dlatego potrzebna nam będzie funkcja sprawdzająca metody statyczne na obiekcie
function Test-StaticMembers
{
    param(
    [ValidateNotNull()]
    $objectToTest
    , 
    [string[]]
    $members)

    [array]$result = $objectToTest.GetMembers() |
        ? {$_.IsStatic -eq $true -and $_.IsPublic -eq $true} |
        ? {$_.IsAbstract -eq $false -and $_.IsConstructor -eq $false} |
        ? { $members -contains $_.Name}

    if( $result.Count -eq $members.Count)
    { return $true}
    
    throw "Not all member are in object. Searching for members: $members"
}
Sprawdzanie obiektu wygląda następująco:
if( ! $User32 -OR !(Test-StaticMembers -objectToTest  $User32   -members 'FindWindowEx','SendMessage' ) )
{
    $User32 = Add-Type -MemberDefinition $User32MethodDefinition -Name 'user32' -Namespace 'Win32' -PassThru
}
Teraz będziemy mogli stworzyć funkcje do wysyłania outputu tekstu do dowolnej aplikacji:
Function Out-Applciation
{
    Param(
        [Parameter(ValueFromPipeline = $true)] 
        [string]
        $text
        ,
        [switch]
        $toAll,
        [switch]
        $passResult
        ,
        [string]
        $appName
    )
    Begin{
            $textArray = @()
            $intPtr = New-Object 'IntPtr' -ArgumentList '0'

            if($toAll)
            {
                $appProcesses = Get-Process -Name $appName -ErrorAction SilentlyContinue 
            }
            else
            {
                $appProcesses =@( Start-Process -FilePath $appName -PassThru )
            }

            #check if process list exist
            if(! $appProcesses)
            {
                Start-Process -FilePath $appName 
                $appProcesses = Get-Process -Name $appName 
            }
    }
    Process{
        $textArray += $text
    }
    End{
        $textString = $textArray  | out-string
        $appProcesses | %{
           $findWindowExTryCounter = 0
           while(!$windowExChild -OR $windowExChild -eq 0)
           {
            $windowExChild =  $User32::FindWindowEx($_.MainWindowHandle, $intPtr,'Edit', $null )
            
            #if FindWindowEx will not work
            $findWindowExTryCounter++
            if($findWindowExTryCounter -gt 100)
            {
                throw "Can not find window $($_.MainWindowHandle) for $appName"
            }
            else
            {
                Start-Sleep -Milliseconds 100 
            }
           }
           $result = $User32::SendMessage($windowExChild, '0x000C', 0,  $textString )
           
           if($passResult) 
           {$result} #if 1 then is has been send
        }
    }
}
Do tego stworzymy pomocna funkcję do wysyłania wyjścia na notepad:
function Out-Notepad
{
    param(
        [switch]
        $toAll,
        [switch]
        $passResult
    )
    Begin{
        $appName = 'notepad'
    }
    End{
        $input | Out-Applciation -toAll:$toAll -passResult:$passResult -appName $appName
    }
}

Dla przykładu zastosowania out-notepad, pomocna nam będzie funkcja numerująca stringi w kolekcji:
function Numerate-String
{
     Param(
        [Parameter(ValueFromPipeline = $true)] 
        [string]
        $text,

        [string]
        $seperator=':'
        )

    begin{
        $count =0
    }
    process{
            $result = "$count $seperator"+ [System.Environment]::NewLine + $text
            $count++
            return $result
        }
}​

A wywołanie może byglądać następująco:
gc applicationName.log | Numerate-String | Out-Notepad

czwartek, 3 lipca 2014

Reaktywacja c++

Od wielu lat nie siedzę w c++, ale ostatnio obejrzałem filmik co ma do zaoferowania nowy C++14 i C++11. Jestem w głębokim szoku. Nowe featury pochodzą od takich popularnych języków jak java, c# czy python. Dawno temu uczyłem się c++, ale kiedy tylko natrafiłem na przejrzysty język c#, to od razu c++ poszedł w niepamięć. Myśłe, że dużo jest osób, którzy tak samo zrobili jak ja. Czy zacznie rosnąć popularność c++ ? Nie wiem.
Poniżej filmik o c++:




piątek, 28 lutego 2014

Rozwiązanie problemu Józefa Flawiusza w PowerShell

Józef Flawiusz walczył w powstaniu żydowskim przeciwko rzymianom. Pewnego razu, żołnierze żydowscy zostali otoczeni przez wojsko rzymskie i 40 powstańców wolało popełnić samobójstwo niż oddać się w niewolę. Żeby nie było łatwo w przewidzeniu kto będzie ostatni w tym czynie, postanowiono, że powstańcy staną w okręgu i co 3 osoba będzie dokonywać samobójstwa. Pomysł samobójstwa nie podobał się Józefowi i chciał stanąć w takim miejscu, aby jako ostatni mógł oddać się do niewoli i przeżyć. Wyznaczenie tego ostatniego miejsca nazywa się problemem Józefa Flawiusza.

Symulację graficzną algorytmu można zobaczyć na tej stronie.

Dla uproszczenia numeracja miejsc jest od 0. Poniżej jest rozwiązanie iteracyjne:
function Solve-JosephusProblem
{
    param(
        [ValidateRange(1,1000)]
        [int]$killEvery = 3,
        [ValidateRange(1,1000)]        
        [int]$soldiers = 40
      )
      $i=1
      $rest=0
      while($i -le $soldiers)
      {
        $rest = ($rest + $killEvery ) % $i 
        $i++
      }
      return $rest
}
Sprawdzenie miejsca dla 10 żołnierzy:
Solve-JosephusProblem -soldiers 10 -killEvery 3
#3


Gdyby samobójca był wybierany co 1 osobę to ostatnia osoba dokona samobójstwa.

Solve-JosephusProblem -soldiers 41 -killEvery 1



Poniżej jest rozwiązanie rekurencyjne:
function Solve-JosephusProblemRec
{
    param(
        [ValidateRange(1,1000)]
        [int]$killEvery = 3,
        [ValidateRange(1,1000)]
        [int]$soldiers = 40
      )

    if($soldiers -eq 1)
    { 
        return 0 
    }
    else
    {
        [int]$recSolve = Solve-JosephusProblemRec -killEvery $killEvery -soldiers ($soldiers-1)
        return (($recSolve +$killEvery  ) % $soldiers )  
    }
}

Nie ukrywam, że dużo pomogła mi strona na wiki opisująca ogólny problem.

Sprawdzamy na którym miejscu powinien Józef Flawiusz się ustawić:
$soldiers =41
$killEvery =3
Solve-JosephusProblem -soldiers $soldiers -killEvery $killEvery  #30
Solve-JosephusProblemRec -soldiers $soldiers -killEvery $killEvery #30

czwartek, 27 lutego 2014

Algorytmy przeszukiwania w PowerShell

Przeglądałem książkę o algorytmach i strukturach danych i postanowiłem zaimplementować przeszukiwanie tablicy w PowerShellu. Założenie jest takie, że może występować w tablicy więcej niż jeden szukany element oraz funkcja przeszukująca powinna zwrócić numery indeksów szukanego elementu. Najprostszy algorytm przeszukiwania sekwencyjnego jest prosty:
function Search-Sequential([array]$arr, $item, [switch]$firstfound)
{
    $found=@()

    for($i=0; $i -lt $arr.Length; $i++)
    {
        if($arr[$i].Equals($item) )
        {
            $found+=$i
            if($firstfound)
            {
                return $found
            }
        }
    }
    return $found
}
Użycie funkcji jest intuicyjne:
$arr = 1,2,3,4,4,4,4,4,5,5,5,6,6,7,8,99,120
Search-Sequential -arr $arr -item 5   #8,9,10
Drugim ciekawym algorytmem przeszukiwania jest przeszukiwanie binarne:
function Search-Binare([array]$arr, $item, [switch]$firstfound)
{
    $low =0
    $high = $arr.Length -1
    $found=@()
    While($low -le $high)
    {
        $middleIdx = [math]::Floor(($low + $high)/2)
        $middleValue = $arr[$middleIdx]

        if($middleValue -lt $item)
        {
            $low = $middleIdx+1
        }
        elseif($middleValue -gt $item)
        {
            $high = $middleIdx -1
        }
        else{
            $found+=$middleIdx

            if(!$firstfound)
            {
                $leftNeighborMiddleIdx = $middleIdx-1

                if($low -le $leftNeighborMiddleIdx)
                {
                    $leftArr =  $arr[$low..$leftNeighborMiddleIdx]
                    $foundOnLeftWithOffset = 
Search-Binare -arr $leftArr -item $item -firstfound:$firstfound
                    $foundOnLeft = $foundOnLeftWithOffset | 
%{ $low + $_  }
                    $found+=$foundOnLeft
                }

                $rightNeighborMiddleIdx = $middleIdx+1
                if($rightNeighborMiddleIdx -le $high)
                {
                    $rightArr = $arr[$rightNeighborMiddleIdx..$high]
                    $foundOnRightWithOffset = 
Search-Binare -arr $rightArr -item $item -firstfound:$firstfound
                    $foundOnRigh = $foundOnRightWithOffset | 
%{$rightNeighborMiddleIdx + $_}
                    $found+=$foundOnRigh
                }
            }
            return $found
        }
    }
}
Niestety musiałem dodać wywołanie rekurencyjne, aby przeszukał sąsiednie elementy. Przeszukiwanie binarne dla tej samej tabeli zwraca te same elementy, ale w innej kolejności:
Search-Binare -arr $arr -item 5  #8,10,9
Postanowiłem napisać jeszcze jeden alg. za pomocą tablicy haszującej. Na samym początku potrzebowalibyśmy funkcji do przekształcenia zwyczajnej tablicy na hashtable:
function ConvertTo-Hashtable(
[array]$arr,
 [scriptblock]$hashFunction = 
$({param($x) return $x.GetHashCode()}))
{
    [array]$hashTable =   @(,$null) * 100
    
    for($i=0; $i -lt $arr.Length; $i++)
    {
        $item = $arr[$i]
        $hashCode = $hashFunction.InvokeReturnAsIs($item)

        $diff = $hashCode - $hashTable.Length
        if($diff -ge 0)
        {
            $hashTable +=  @(,$null) * ($diff +1)
        }

        if(-not($hashTable[$hashCode]))
        {
            $hashTable[$hashCode] =@()
        }

        $hashTable[$hashCode] += $i
    }
    return $hashTable
}
Warto zauważyć tutaj jaka jest funkcja haszująca. Korzystamy z ogólnodostępnej funkcji GetHashCode.
Funkcja haszująca jest zapisana w script bloku. Konwersja i wyszukiwanie wygląda następująco:
$arr = 1,2,3,4,4,4,4,4,5,5,5,6,6,7,8,99,120
$hashtable  = ConvertTo-Hashtable -arr $arr
Search-Hashed -arr $hashtable -item 5 #8,9,10

Natomiast funkcja przeszukująca tablice haszującą ma 2 linijki implementacji:
function Search-Hashed
([array]$arr, $item, 
[scriptblock]$hashFunction= 
$({param($x) return $x.GetHashCode()}))
{
    $hashCode = $hashFunction.InvokeReturnAsIs($item)
    return $arr[$hashCode]
}
Zamiast domyślnej funkcji haszującej można stworzyć swoją własną. Mój przykład dla znaków to:
$hashFunction = {param($x) [int][char]$x}
A wywołanie wygląda następująco:
$arr = 'A','V','b','b','C'
$hashtable = ConvertTo-Hashtable -arr $arr -hashFunction $hashFunction
Search-Hashed -arr $hashtable -item 'b' -hashFunction $hashFunction  #2,3
Równie dobrze mogę zastosować własną funkcję haszującą string:
$hashFunctionString = {param($x)  [int[]][char[]]$x-join ''}
$hashFunctionString.InvokeReturnAsIs("Hello Arek ") #72101108111326511410110732
Oczywiście nie mogę stworzyć tak wielkiej tablicy, ale może coś się wymyśli :)

wtorek, 25 lutego 2014

Symbol Newtona w PowerShell

Chciałem poćwiczyć z symbolem Newtona, więc postanowiłem napisać skrypt w PS, aby porównać te sposoby uzyskiwania współczynnika dwumianowego.
Oczywiście do symbolu Newtona będziemy potrzebowali funkcję zwracającą wartość silni.
function Get-Factorial([int]$n)
{
    if($n -lt 1) {
        return 1
    }
    $facttorialRec = Get-Factorial($n -1)
    return ($n * $facttorialRec )
}
Jak na razie funkcja oblicza silnie rekurencyjnie (ale mam w planach dodać jeszcze silnie obliczoną w sposób iteracyjnie).

Mamy też funkcję obliczającą symbol Newtona za pomocą rekurencji:
function Get-BinomialCoefficientRec([int]$n, [int]$k)
{
    if($k -lt 0 -OR $k -gt $n)
        {return 0}

    if($k -eq 0 -OR $k -eq $n -OR $n -lt 1)
        {return 1} 

    $diff = $n - $k
    if($k -gt $diff)
    {
        $k = $diff
    }
    
    $binomial1 = Get-BinomialCoefficientRec -n ($n-1) -k $k
    $binomial2 = Get-BinomialCoefficientRec -n ($n-1) -k ($k-1)
    return $binomial1 + $binomial2
}


Oraz funkcję korzystającą z tożsamości algebraicznej:
function Get-BinomialCoefficientIteratively([int]$n, [int]$k)
{
    if ($k -lt 0 -or  $k -gt $n)
    {return 0}
 
    if($k -eq  0 -or  $k -eq  $n)
    { return 1}

    $diff = $n - $k
    if ($k -gt $diff)
    {  $k = $diff }
    
    $prod = 1
    for($i=1; $i -le $k; $i++)
    {
        $prod = $prod * (  ($n - $i + 1) / $i  )    
    }

    return $prod
}

Możemy wyliczyć symbol Newtona na ze wzoru Sterlinga. Wzór ten stosuje się dla bardzo dużych liczb, gdyż błędy względne są małe.
function Get-StirlingApproximation([int]$n)
{

    $numDivE = $n / [math]::E
    $const = [math]::Sqrt(2.0 * $n * [math]::PI)
    $result = $const * ([math]::Pow(($numDivE) ,$n))   
    if($result -eq 0)
        {return 1}
    return $result
}


Główna funkcja, która agreguje funkcje zliczające współczynnik dwumianowy jest przedstawiona poniżej:

function Get-BinomialCoefficient
{
    param(
    [ValidateSet('factorial','recursive', 'multiplicative', 'stirling'  )]
    [string]$arg='factorial',

    [Parameter(Mandatory=$true, Position=0)]  
    [int]$n,

    [Parameter(Mandatory=$true, Position=1)]  
    [int]$k
    )

    if($arg -eq 'factorial')
    {
         $upperFractal = Get-Factorial($n) 
         $lowerFractalK = Get-Factorial ($k) 
         $lowerFractalNK=  Get-Factorial($n- $k)
         return $upperFractal  / ($lowerFractalK * $lowerFractalNK)
    }
    elseif($arg -eq 'stirling')
    {
        $aproxN = Get-StirlingApproximation($n)
        $aproxK = Get-StirlingApproximation ($k)
        $aproxNK = Get-StirlingApproximation($n- $k)
        return  $aproxN/ ( $aproxK  *$aproxNK )
    }
    elseif($arg -eq 'recursive')
    {
        return Get-BinomialCoefficientRec -n $n -k $k
    }
    elseif($arg -eq 'multiplicative')
    {
        return Get-BinomialCoefficientIteratively -n $n -k $k
    }

    throw 'Wrong argument in parameter $arg'
}


A funkcja do pomiaru wygląda następująco:
function Measure-BinomialCoefficient([int]$to)
{
    $Nenum = 1..$to
    $Nenum | %{
        $n = $_
        $Kenum = 1..$n 
        $Kenum | %{
           $k = $_
           $factorialMeasure =         
(Invoke-Command  {  Get-BinomialCoefficient -arg factorial -n $n -k $k       })

           $multiplicativeMeasure =    
(Invoke-Command  {  Get-BinomialCoefficient -arg multiplicative -n $n -k $k  })

           $recursiveMeasure =         
(Invoke-Command  {  Get-BinomialCoefficient -arg recursive -n $n -k $k       })

           $stirlingMeasure =          
(Invoke-Command  {  Get-BinomialCoefficient -arg stirling -n $n -k $k        })


            return new-object PSObject | 
                
Add-Member -PassThru -MemberType NoteProperty 
-Name  n  -Value $n |
               
Add-Member -PassThru -MemberType NoteProperty 
-Name  k  -Value $k |
                
Add-Member -PassThru -MemberType NoteProperty 
-Name  factorial  -Value $factorialMeasure |
                
Add-Member -PassThru -MemberType NoteProperty 
-Name  multiplicative  -Value $multiplicativeMeasure |
                
Add-Member -PassThru -MemberType NoteProperty 
-Name  recursive  -Value $recursiveMeasure |
                
Add-Member -PassThru -MemberType NoteProperty 
-Name  stirling  -Value $stirlingMeasure
        }
    }
}

Wyniki tych wywołań są przedstawione poniżej. Co prawda wzór Stirling nie daje dokładnej wartości, ale jestem pod wrażeniem przybliżenia wartości symbolu Newtona.



Aby uzyskać czasy działania algorytmów to wystarczy zamienić słowa Invoke na Measure oraz dodać TotalMilliseconds - pomiary są w milisekundach.
           $factorialMeasure =         
(Measure-Command  {  Get-BinomialCoefficient -arg factorial -n $n -k $k       }).TotalMilliseconds

           $multiplicativeMeasure =    
(Measure-Command  {  Get-BinomialCoefficient -arg multiplicative -n $n -k $k  }).TotalMilliseconds

           $recursiveMeasure =         
(Measure-Command  {  Get-BinomialCoefficient -arg recursive -n $n -k $k       }).TotalMilliseconds

           $stirlingMeasure =          
(Measure-Command  {  Get-BinomialCoefficient -arg stirling -n $n -k $k        }).TotalMilliseconds

A poniżej wykres przedstawiający czasy działania algorytmów (dla różnych n i k):



Gdybyśmy ograniczyli ilość danych poprzez wprowadzenie parametru k jako połowa parametru n oraz przedstawili dane na wykresie w skali logarytmicznej to mamy taki wykres:

Co dla mnie jest dziwne, to czasy obliczeń symbolu Newtona za pomocą wzoru Sterlinga oraz dla iteracyjnego sposób wyliczenia się zmniejszają :)
Takie rzeczy to tylko w PS.


sobota, 15 lutego 2014

Skróty klawiszowe do VS

Jak wiadomo skróty klawiszowe są bardzo przydatne bo polepszają naszą produktywność, więc dobrze jest mieć skróty dopasowane do własnych potrzeb. Z indywidualnymi skrótami klawiszowymi jest jeden problem - osoba, która nie zna twoich skrótów może mieć problemy z obsługą środowiska programistycznego, już nie mówiąc o takim dziwnym tworze jak Ping-Pong Pair Programming. Ja mam skróty dopasowane do siebie i osoby na moim komputerze nie wiedzą jak ja mogę w ogóle kodować :)

Poniżej jest tabelka ze skrótami. Wywołanie polecenia składa się z kombinacji skrótów. Pierwszy skrót oznacza grupę, a drugi skrót już konkretne polecenie. Jeżeli chcesz odpalić polecenie debugowania to korzystasz z grupy RUN (CTRL + R) i polecenie Debug.Start (CTRL + D). Jeżeli bez chcesz uruchomić bez debagowania to pod tej samej grupie jest polecenie Debug.StartWithoutDebugging (CTRL + SHIFT + D).
Niektóre skróty już coś oznaczają (CTRL + F czy CTRL + A), więc musiałem dołożyć dodatkowy klawisz.



Plik z ustawieniami importu w Visual Studio jest poniżej:


Moje skróty ewoluują, Wcześniej miałem skrót do zen codingu, czy do GhostDoc'a. Czasami skróty są zależne od projektu.

poniedziałek, 10 lutego 2014

Graf w PowerShell

Dzisiaj troszkę się namęczyłem z implementacją grafu skierowanego w PowerShellu. Co prawda dużo łatwiej było by mi zaimplementować graf w c#, ale w ramach ćwiczeń ... chciałem się namęczyć :)

Poniżej jest moja implementacji grafu skierowane
Na początku tworzymy obiekt, który będzie zawierał listę krawędzi:
function Create-Graph
{
    return new-object PSObject | 
            Add-Member -PassThru -MemberType NoteProperty -Name  Edges  -Value @()  -TypeName "array" 
}

Do dodawania krawędzi potrzebujemy dodatkowych funkcja. Zakładamy, że krawędź będzie zawierała informację o połączeniu dwóch wierzchołków oraz wartości tego połączenia.
function Create-Edge
{
    param(
        [ValidateNotNullOrEmpty()] 
        [string]$from,
        [ValidateNotNullOrEmpty()]
        [string]$to,
        [int]$weight=1
    )
    return @{
        From  = $from;
        To = $to;
        Weight = $weight;
    }
}

function Add-Edge
{
    param(
        [Parameter(Mandatory=$true, Position=1,ValueFromPipeline=$true)]  
        [ValidateNotNull()]    
        $graph,

        [Parameter(Mandatory=$true, Position=2)] 
        [ValidateNotNull()]     
        $edge
      )
    process{
        $graph.Edges +=$edge
        return $graph
    }
}



Wtedy poniższy kod tworzy graf skierowany:
$graph  = Create-Graph | 
    Add-Edge -edge (Create-Edge A B  1 ) |
    Add-Edge -edge (Create-Edge A C  5) |
    Add-Edge -edge (Create-Edge A D  3) |
    Add-Edge -edge (Create-Edge A E  11) |
    Add-Edge -edge (Create-Edge B C  9) |
    Add-Edge -edge (Create-Edge B D  1) |
    Add-Edge -edge (Create-Edge B E  2) |
    Add-Edge -edge (Create-Edge C D  6) |
    Add-Edge -edge (Create-Edge C E  4) |
    Add-Edge -edge (Create-Edge D E  3) 
Do sprawdzenia nazw wierzchołków pomocne będą poniższe funkcje:
function Get-EdgesNumber
{
    param(
        [Parameter(Mandatory=$true, Position=1)] 
        [ValidateNotNull()]     
        $graph
      )
    return $graph.Edges.Count
}

function Get-Vertices
{
    [cmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] 
    param(
        [Parameter(Mandatory=$true, Position=1)]
        [ValidateNotNull()]      
        $graph
      )
      Write-Verbose "Getting number of Vertices in graph"

      [array] $toVertices = $graph.Edges.To 
      [array] $fromVertices =   $graph.Edges.From
      return [array](($toVertices + $fromVertices) | select -Unique | sort)
}

function ConvertFrom-Graph
{
    [cmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] 
    param(
        [Parameter(Mandatory=$true, Position=1,ValueFromPipeline=$true)]
        [ValidateNotNull()] 
        $graph
      )
    process {
        $graph.Edges | %{
              $from =  $_.From 
              $to = $_.To
              $weight = $_.Weight
              Write-Verbose "Weight is $weight for:"
              "$from -> $to"
              }
    }
}

A poniżej przedstawione są wywołania tych funkcji:
Get-EdgesNumber $graph

Get-Vertices $graph

$graph | ConvertFrom-Graph


Oprócz reprezentacji grafu za pomocą kolekcji krawędzi można przedstawić graf za pomocą macierzy:
function ConvertTo-Matrix
{
   param(
        [Parameter(Mandatory=$true, Position=1,ValueFromPipeline=$true)]
        [ValidateNotNull()] 
        $graph
      )
  process{
        $vertices= Get-Vertices $graph
        $dim  = $vertices.Length
        [object[][]]$matrix =  Create-ZeroMatrix $dim
   
        $graph.Edges | % {
            $from = $_.From
            $to = $_.To
            $weight = $_.Weight

            $fromIndex = $vertices.IndexOf($from)
            $toIndex = $vertices.IndexOf($to)

            $matrix[$fromIndex][$toIndex]= $weight
        }
        return $matrix
    }
}

function Create-ZeroMatrix 
{
    param(
        [ValidateScript({$_ -ge 0})]
        [int]$dim
      )
      $matrix = New-Object 'object[][]' $dim,$dim

      for($i =0; $i -lt $dim; $i++ )
      {
        for($j=0; $j -lt $dim ; $j++)
        {
            $matrix[$i][$j] = 0
        }
      }
      return $matrix
}

function ConvertFrom-Matrix 
{
    [cmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] 
    param(
    [Parameter(Mandatory=$true, Position=1)]
    [object[][]]$matrix,
    [string]$seperator = ' '
      )
    for($i=0; $i -lt $matrix.Length; $i++)
    {
        Write-Verbose $i
        $(($matrix[$i]) -join $seperator)
    }
}

Konwersja kolekcji krawędzi na macierz jest przestawiona poniżej:
$matrix =  $graph | ConvertTo-Matrix
ConvertFrom-Matrix $matrix 
Oczywiście jeszcze wiele muszę poprawić m.in. konwersja z reprezentacji macierzowej do kolekcji krawędzi, modyfikacja wierzchołków, modyfikacja grafu skierowanego na graf nieskierowanego, walidacja czy wszystkie punkty są ze sobą połączone, ujednolicenie operacji na grafie itd.

niedziela, 2 lutego 2014

Skrypt w parametrach domyślnych w PS

Jak piszę kod w C# i mam metodę z możliwością ustawienia domyślnych parametrów to staram się użyć domyślnych parametrów (jednak, nie polecam robienia czegoś takiego dla interfejsów). Czasami problem pojawia się, kiedy do parametru chciałbym przypisać wartość z innej metody. Składnia języka C# na to nie pozwala:


Domyślne parametry mają pewne ograniczenia m.in.:
- nie mogą posiadać modyfikatora ref, out, czy params
- muszą być stałymi w czasie kompilacji
- nie mogą być metodami, właściwościami ani wyrażeniem
- domyślne parametry muszą być definiowane po wymaganych parametrach w metodzie

Co mi się bardzo podoba w PowerShellu to możliwość wywoływania polecenia razem z domyślnymi wartości ( mogą to być bloki skryptów).
PS jest dużo bardziej elastyczny językiem programowania i możemy wprowadzić wymóg wprowadzenia parametru user oraz podania hasła, które na ekranie będzie 'ukryte' :)
function Do-SomeThing
{
    param(
    [string] $computerName = $env:computerName,
    [string] $user =  $(throw "Argument user is not set"),
    [string] $passwd =  $(Get-Password)
    )
    Write-Host "user:$user passwd:$passwd on computer:$computerName"
} 

Przy wywołaniu prostego polecenia dostajemy błąd:
PS>Do-SomeThing 
Argument user is not set

Kiedy wywołamy polecenie z użytkownikiem to dostajemy okienko z możliwością wpisania hasła


Możesz wywołać funkcję z hasłem i nazwą komputera:
Do-SomeThing -user "Arek" -passwd "Abc" -computerName "MyPc"

No i jeszcze funkcja do pobierania hasła:
function Get-Password
{
    $securePass = Read-Host "Enter Password" -AsSecureString
    $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePass)
    $pass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
    [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
    return $pass
}
Oczywiście w niektórych przypadkach można użyć Parameter(Mandatory) oraz walidatorów (ValidateScript), ale chciałem Ci przedstawić co można zrobić z domyślnymi parametrami.

sobota, 1 lutego 2014

Czytanie mojego kodu

Kolega przesłał mi zdjęcie przedstawiający co myśli, gdy czyta mój kod :)
No cóż, nikt nie jest idealny



niedziela, 10 listopada 2013

Zabawa z blockly

Pamiętasz może Logo Komeniusza - prosty graficzny język programowania powstały na Massachusetts Institute of Technology w USA w latach '70, aby wprowadzić dzieci w świat programowania :) Super idea i gdyby nie ten język, wiele dzisiejszych programistów (w tym ja - tak uczyłem się go w gimnazjum i jestem z tego dumny ) nie robiło by togo, co teraz robi.
Znalazłem projekt blockly, który bardzo przypomina programowanie żółwia.

Oczywiście można zagrać w układnie algorytmu do rozwiązywania labiryntów.



Oprócz tego blockly można programować za pomocą schematów blokowych:





Istnieją też projekty, w których blockly jest wykorzystywany do bardziej trudniejszych problemów.


Myślę, że za kilka (kilkanaście) lat każda osoba będzie w stanie zaprogramować swojego 'robota' za pomocą schematu blokowego.

piątek, 8 listopada 2013

Róźne zwracane wartości w Moq

Moq jest fantastyczne, bardzo przejrzyste, proste w nauczeniu się oraz popularne narzędzie do mockowania obiektów. Przedstawiam Ci prosty extension metod dla Moq. Metoda dzięki której mock zwraca rożne wartości z podanej listy za każdym wywołaniem.
Będziemy mockować interfejs:
    public interface INumerable
    {
        int GetNumber { get; }
    }
Standardowo, aby uzyskać różne wartości przy wywołaniu należało wykonać coś takiego:
var listOfReturnedelements = new List<int> {1, 4, 1, 8};
int number = 0;
var numMock= new Mock<INumerable>();

numMock
   .Setup(x=>x.GetNumber)
   .Returns(() => listOfReturnedelements[number])
    .Callback(() => number++)
;
Musimy zdefiniować dodatkową zmienną określająca index listy oraz wywołać metodę Callback, aby zwiększać ten index.
Za każdym razem jak wywołamy num.GetNumber() to dostajemy inną wartość tablicy. Poniżej przykład użycia:
var num = numMock.Object;
var list = new List<int>();
for (int i = 0; i < 4; i++)
{
    list.Add(num.GetNumber);                
}

Zamiast tego to możesz skorzystać z metody:
public static void ReturnsInOrder<T, TResult>(this ISetup<T, TResult> setup, IEnumerable<TResult> results)
    where T : class
{
            setup.Returns(new Queue<TResult>(results).Dequeue);
}
A kod ustawiający obiekt mockujący wygląda następująco:
numMock
.Setup(x=>x.GetNumber)
.ReturnsInOrder(listOfReturnedelements)

Troszkę krótkie wywołanie, ale bardzo mi się podoba użycie kolejki w rozwiązaniu.

niedziela, 3 listopada 2013

Testowanie prywatnych metod

Ok, ważna zasada testowania prywatnych metod - prywatne metody nie testuje się!!!. Traktuje się je jako szczegóły implementacji, które nie muszą być przedstawiane, a testy tych metod są poprzez metody publiczne. Jednak, jeżeli chcielibyśmy przetestować te "szczegóły implementacji" to moglibyśmy ukrytą implementację wyekstraktować do oddzielnej klasy. Inne możliwości przedstawiam Ci w tym wpisie.


Przypuśćmy, że mamy klasę, która w logice zwraca GUID typu string:
public class GuidReturner
{
        public string GetGuidString(int arg)
        {
            var cmplexStr = GetGuid(arg);
            return cmplexStr == Guid.Empty ? "" : cmplexStr.ToString();
        }
 
        private Guid GetGuid(int arg)
        {
            return arg == 0 ? GetEmptyGuid() : Guid.NewGuid();
        }
 
        private static Guid GetEmptyGuid()
        {
            return Guid.Empty;
        }
}
 
W tej klasie mamy prywatną metodę GetGuid(int) : Guid. Aby ją przetestować to możemy zmienić prawa dostępu na internal. Wtedy wystarczy zastosować InternalsVisibleToAttribute i wpisać do AssemblyInfo.cs
[assembly: InternalsVisibleTo("YourLib.Tests")]
Minusem takiego rozwiązania jest modyfikacja zależności między projektem produkcyjnym, a projektem testującym, przymus pamiętania o zmianie stringa przy zmianie nazwy assembly oraz dodawanie dodatkowych InternalsVisibleTo dla każdego projektu testowego.

Inną możliwością testowania prywatnej metody jest zmiana prawa dostępu na protected i dziedziczenia po tej klasie. Dla naszego przykładu mamy:
public class GuidReturner
{
        protected Guid GetGuid(int arg)
        {
            return arg == 0 ? GetEmptyGuid() : Guid.NewGuid();
        }
}
A w projekcie w którym mamy testy tworzymy klasę testowalną:
public class TestableGuidReturner : GuidReturner
{
        public Guid TestGetGuid(int arg)
        {
            return base.GetGuid(arg);
        }
}
Minusem takiego rozwiązania jest tworzenie dodatkowej klasy testowalnej. Ma to też swoje plusy, gdzie możemy dodać dodatkową implementację, logowanie lub walidację dla testów.

Jeżeli nie podobają Ci się sposoby zmiany praw dostępu to można użyć mechanizmów refleksji. Do tego użyjemy obiektu dynamic.
public class NonPublicInvoker<T> : DynamicObject
{
        private readonly T _closeType;
        
        public NonPublicInvoker(T closeType )
        {
            _closeType = closeType;
        }
 
        public override bool TryInvokeMember(InvokeMemberBinder binder, 
                            object[] args, out object result)
        {
            result = _closeType.GetType()
                     .InvokeMember(binder.Name, 
                      BindingFlags.Instance 
                        | BindingFlags.NonPublic 
                        | BindingFlags.InvokeMethod
                     ,null, _closeType, args);
            return true;
        }
}
Test może wyglądać następująco:
[Test]
public void Dynamic_It_should_return_empty_guid_for_0()
{
      dynamic sbc = new NonPublicInvoker<GuidReturner>(new GuidReturner());
      Assert.AreEqual(sbc.GetGuid(0), Guid.Empty);
}
Do tego stwórzmy dynamika dla metod statycznych:
public class StaticNonPublicInvoker<T> : DynamicObject
{
        public override bool TryInvokeMember(InvokeMemberBinder binder, 
                             object[] args, out object result)
        {
            result = typeof(T)
                          .InvokeMember(binder.Name, 
                           BindingFlags.NonPublic 
                              | BindingFlags.Static 
                              | BindingFlags.InvokeMethod
                          ,null, null, args);
            return true;
        }
}
[Test]
public void Dynamic_It_should_return_empty_guid_for_static_private_method()
{
            dynamic sbc = new StaticNonPublicInvoker<GuidReturner>();
            Assert.AreEqual(sbc.GetEmptyGuid(), Guid.Empty);
}

W poniższym filmiki jest przedstawiony podobny sposób testowania prywatnych metod za pomocą obiektu PrivateObject



piątek, 1 listopada 2013

BDD Test Template

Wcześniej pisałem już o Korniszoku i BDD, ale teraz chciałbym przedstawić Ci template z jakim u mnie w pracy się pracuje. Template ten jest w modzie BDD. Testuje jedną klasę przy jednym zachowaniu. Jeżeli mielibyśmy n ważnych zachowań to potrzebujemy stworzyć n takich klas testowych.
Template wygląda następująco:
   [Category("BDD_MySuperClass")]
    public class when_we_run_special_methods : InstanceSpecification<MySuperClass>
    {
        protected override MySuperClass Create_subject_under_test()
        {
            return new MySuperClass();
        }

        [Test]
        public void It_should_do_special_stuff()
        {
            
        }
    } 


Cała magia jest w dwóch klasach abstrakcyjnych:
    public abstract class Specification
    {
        [SetUp]
        public virtual void BaseSetUp()
        {}

        [TearDown]
        public virtual void BaseTearDown()
        {}

        [DebuggerStepThrough]
        protected virtual void Establish_context()
        {}

        [DebuggerStepThrough]
        protected virtual void Because()
        {}

        [DebuggerStepThrough]
        protected virtual void Dispose_context()
        {}

        [DebuggerStepThrough]
        protected virtual void Initialize_subject_under_test()
        {}
    }

I druga generyczna klasa:
    public abstract class InstanceSpecification<TSubjectUnderTest> : Specification
    {
        protected TSubjectUnderTest SubjectUnderTest { get; private set; }

        protected abstract TSubjectUnderTest Create_subject_under_test();

        public override void BaseSetUp()
        {
            Establish_context();
            Initialize_subject_under_test();
            Because();
        }

        public override  void BaseTearDown()
        {
            Dispose_context();
        }

        protected override  void Initialize_subject_under_test()
        {
            SubjectUnderTest = Create_subject_under_test();
        }
    }


'Super' przykład dla StringBuilder jest przedstawiony poniżej:
[Category("BDD_StringBuilder")]
[TestFixture]
public class when_we_insert_char_into_empty_stringBuilder : InstanceSpecification<StringBuilder>
{
    public string SubjectUnderTestString { get { return SubjectUnderTest.ToString(); } }

    protected override StringBuilder Create_subject_under_test()
    {
        return new StringBuilder();
    }

    protected override void Because()
    {
        SubjectUnderTest.Insert(0, "first word");
    }

    [Test]
    public void It_should_start_with_word_first()
    {
        Assert.IsTrue(SubjectUnderTestString.StartsWith("first"));
    }

    [Test]
    public void It_should_not_be_empty_string_because_words_ware_added()
    {
        Assert.NotNull(SubjectUnderTest);
        Assert.IsTrue(SubjectUnderTestString.Any()); //any char
    }
} 


SubjectUnderTestString to pole pomocnicze. Przykład przedstawia wzorzec AAA (Arrange-Act-Assert). Metoda Create_subject_under_test() przygotowuje obiekt, metoda Because() wykonuje pewne zachowania na tym obiekcie i mamy dwie metody testowe.

Więcej szczegółów o testowaniu w stylu BDD możesz zaleź na elegantcode.com. Możesz pobrać code snippet z MyCodeSnippets.

niedziela, 27 października 2013

80% i nie mniej

Przeczytałem świetny tekst o testowaniu na stronie Alberto Savoia (Artima Developera) i uważam, że kwestia pokrycia kodu wciąż powraca, więc zamieszczę spolszczoną wersje Mądrości Testvusa o pokryciu.



Pewnego ranka, młody programista rozmawiał z wielkim mistrzem.
"Jestem gotowy pisać testy jednostkowe. Do jakiego pokrycia kodu powinienem dążyć"
Wielki mistrz odpowiedział:
"Nie przejmuj się pokryciem, po prostu pisz dobre testy"
Młody programista uśmiechnął się, podziękował i poszedł.


Później, tego samego dnia, inny programista zadał to samo pytanie.
Wielki mistrz wskazał na garnek wrzącej wody i powiedział.
"Ile ziarenek ryżu powinienem włożyć do garnka?"
Programista spojrzał zdziwiony i odpowiedział:
"Skąd mogę wiedzieć? To zależy ile ludzi chcesz nakarmić, jak bardzo są głodni, jakie inne jedzenie podajesz, ile masz dostępnego ryżu i tak dalej"
"Dokładnie" - odpowiedział wielki mistrz
Drugi programista uśmiechnął się, podziękował i poszedł.


Pod koniec dnia, trzeci programista przyszedł i zadał to samo pytanie odnośnie pokrycia kodu.
"Osiemdziesiąt procent i nie mniej" - odpowiedział mistrz surowym głosem uderzając ręką o stół
Trzeci programista uśmiechnął się, podziękował i poszedł.


Po ostatniej odpowiedzi, młody uczeń podszedł pod wielkiego mistrza.
"Wielki mistrzu, dzisiaj słyszałem różne Twoje odpowiedzi do tego samego pytania o pokryciu kodu. Dlaczego?"
Wielki mistrz wstał z krzesła.
"Choć napij się ze mną świeżej herbaty i porozmawiajmy o tym"


Po tym jak zaparzyli zieloną herbatę - wielki mistrz zaczął:
"Pierwszy programista jest nowy i dopiero zaczyna znajomość z testowaniem. Teraz ma dużo kodu, który nie ma testów. Długą drogę ma do przebycia, a koncentrując się w tej chwili na pokryciu kodu byłoby przygnębiające i bezużyteczne. Lepiej jest dla niego by przyzwyczaił się do pisania kodu i testów. Później będzie się martwił o pokrycie kodu"


"Drugi programista jest doświadczony w programowaniu i testowaniu. Kiedy odpowiedziałem pytająco, 'ile ziarenek ryżu powinno się umieścić w garnku', to pomogłem mu zrozumieć, że ilość wymaganych testów jest uzależnione od wielu czynników, a on wie lepiej niż ja - bo to jest jego kod. Nie mam jednej prostej odpowiedzi i on jest wystarczająco bystry, aby o tym nie wiedział".


Rozumiem - odpowiedział młody uczeń - ale skoro tam nie ma pojedynczej odpowiedzi, to dlaczego odpowiedziałeś trzeciemu programiście 'Osiemdziesiąt procent i nie mniej'?
Wielki mistrz zaczął się śmiać tak mocno i głośno, że było widać, że wypił coś więcej niż zieloną herbatę.
"Trzeci programista chciał tylko prostych odpowiedzi - nawet, gdyby nie było prostych odpowiedzi, a i tak nie będzie ich przestrzegał"
Młody uczeń i siwy wielki mistrz skończyli pić herbatę w kontemplacyjnej ciszy.


Inne mądrości Testvusa można poczytać na stronie artima lub w wersji pdf.

piątek, 25 października 2013

Testowanie po IEquatable

Tak się zastanawiam nad testowaniem obiektów, które można przyrównać do obiektów tego samego typu (metoda Equals). Przypuśćmy, że masz klasę, która implementuję IEquatable<WrappedString>.
public class WrappedString : IEquatable<WrappedString>
{
        public string StringValue { get; set; }
 
        public WrappedString(string stringValue)
        {
            StringValue = stringValue;
        }
 
        public bool Equals(WrappedString other)
        {
            return this.StringValue == other.StringValue;
        }
}
Każda klasa implementująca IEquatable<WrappedString> powinna spełniać parę standardów, m.in. jeżeli T jest strukturą to domyślna wartość powinna zwrócić tą samą wartość, a kiedy obiekt klasy porównujemy do null zawsze powinna zwracać fałsz.
 public abstract class EquatableRelation<TType> 
        where TType : IEquatable<TType>
{
        public abstract TType GetItemX();  //object to test
        private bool IsValueType()
        {
            return typeof (TType).IsValueType;
        }

  #region Default values
        protected virtual void It_should_return_false_because_comparing_to_null()
        {
            if (!IsValueType())
            {
                TType number1 = GetItemX();
                Assert.IsFalse(number1.Equals(null));
            }
        }
 
        protected virtual void It_should_return_true_for_default_value_in_valueType()
        {
            if (IsValueType())
            {
                var value1 = default(TType);
                var value2 = default(TType);
                Assert.IsTrue(value1.Equals(value2));
            }
        }
        #endregion
}

Obiekt powinien spełniać warunki relacji równoważności.
 public abstract class EquatableRelation<TType> 
        where TType : IEquatable<TType>
{
public abstract TType GetItemX();
public abstract TType GetItemY();
public abstract TType GetItemZ();

#region relation
//relacja zwrotna
protected virtual void It_should_be_reflexive_relation()
{
            TType number1 = GetItemX();
            if (!IsValueType())
            {
                Assert.IsNotNull(number1);
            }
            Assert.IsTrue(number1.Equals(number1));
}

 //relacja symetryczna
protected virtual void It_should_be_symmetric_relation()
{
            TType number1 = GetItemX();
            TType number2 = GetItemY();
 
            bool number1To2 = number1.Equals(number2);
            bool number2To1 = number2.Equals(number1);
            Assert.AreEqual(number1To2, number2To1);
}

//relacja przechodnia
protected virtual void It_should_be_Transitive_relation()
{
            TType number1 = GetItemX();
            TType number2 = GetItemY();
            TType number3 = GetItemZ();
 
            Assert.IsTrue(number1.Equals(number2));
            Assert.IsTrue(number2.Equals(number3));
            Assert.IsTrue(number1.Equals(number3));
 
}
#endregion
}
Oprócz tego zawsze powinno się zwracać taką samą wartość porównania:
 public abstract class EquatableRelation<TType> 
        where TType : IEquatable<TType>
{
private const int NumberOfRepeatedTestRuns = 10;

protected virtual void It_should_return_always_the_same_value_for_Equals()
{
            var bools= new List<bool>();
            for (int i = 0; i < NumberOfRepeatedTestRuns; i++)
            {
                TType number1 = GetItemX();
                TType number2 = GetItemY();
                bools.Add(number1.Equals(number2));
            }
            Assert.IsTrue(bools.All(x => x));
}
 
protected virtual void It_should_return_always_the_same_value_for_reflexive()
{
            var bools= new List<bool>();
            for (int i = 0; i < NumberOfRepeatedTestRuns; i++)
            {
                TType number1 = GetItemX();
                TType number2 = GetItemX();
                bools.Add(number1.Equals(number2));
            }
            Assert.IsTrue(bools.All(x => x));
}

protected virtual void It_should_return_always_true_for_default_values_in_valueType()
{
            if (IsValueType())
            {
                for (int i = 0; i < NumberOfRepeatedTestRuns; i++)
                {
                    var value1 = default(TType);
                    var value2 = default(TType);
                    Assert.IsTrue(value1.Equals(value2));
                }
            }
}
}
Oraz w różnym czasie domyślna wartość powinna zwracać tą samą wartość
 public abstract class EquatableRelation<TType> 
        where TType : IEquatable<TType>
{
 protected virtual void It_should_return_always_the_same_value_for_default_value_in_valueType_for_diffrent_timeTics() //super długa nazwa
{
            if (IsValueType())
            {
                Random r= new Random(12345);
                var defaultvalues = new List<TType>();
                for (int i = 0; i < NumberOfRepeatedTestRuns; i++)
                {
                    Thread.Sleep(r.Next(100,500));
                    defaultvalues.Add(default(TType));
                }
                Assert.IsTrue(defaultvalues.All(x => x.Equals(default(TType))));
            }
        }
}
Ok, to teraz masz standardy jakie powinien spełniać każdy obiekt implementujący IEquatable<WrappedString>. Możemy do tego stworzyć interfejs z tym standardem.
public interface IStandardEquatable
{
  void It_should_fulfill_IEquatable_standards();
}

 public abstract class EquatableRelation<TType>  :IStandardEquatable
        where TType : IEquatable<TType>
{
 public virtual void It_should_fulfill_IEquatable_standards()
{
            It_should_return_true_for_default_value_in_valueType();
            It_should_return_false_because_comparing_to_null();
            It_should_be_reflexive_relation();
            It_should_return_always_the_same_value_for_reflexive();
            It_should_be_coreflexive_relation_because_same_value();
            It_should_return_always_the_same_value_for_Equals();
            It_should_be_symmetric_relation();
            It_should_be_Transitive_relation();
            It_should_return_always_the_same_value_for_default_value_in_valueType();
            It_should_return_always_true_for_default_values_in_valueType();
            It_should_return_always_the_same_value_for_default_value_in_valueType_for_diffrent_timeTics();
}
}
A nasz pierwszy abstrakcyjny test
 public abstract class EquatableRelationTest<TType>  : EquatableRelation<TType>
        where TType : IEquatable<TType>

{
[Test, Explicit("Run test which are required for standardization")]
public override void It_should_fulfill_IEquatable_standards()
{
            base.It_should_fulfill_IEquatable_standards();
}
}

Jeżeli będziesz chciał przetestować int to możesz:
[TestFixture]
public class IntEquatableTest : EquatableRelationTest<int>
{
        public override  int GetItemX()
        {
            return 1;
        }
 
        public override int GetItemY()
        {
            return 1;
        }
 
        public override int GetItemZ()
        {
            return 1;
        }
}
Tak samo dla WrappedString
[TestFixture]
public class WrappedStringEquatableTest :  EquatableRelationTest<WrappedString>
{
        public override WrappedString GetItemX()
        {
            return new WrappedString("Abc");
        }
 
        public override WrappedString GetItemY()
        {
            return new WrappedString("Abc");
        }
 
        public override WrappedString GetItemZ()
        {
            return new WrappedString("Abc");
        }
}
Można skorzystać z TestCasów
[TestFixture]
public class WrappedStringEquatableTest3
{
        [TestCase("Abc", "Abc", "Abc")]
        [TestCase("Abc ", "abc", "Abc")]
        public void It_should_be_eq(string str1, string str2, string str3)
        {
            IEqStdFulfillable tuple =
                new EquatableRelationTuple(new WrappedString(str1), 
                                                          new WrappedString(str2),
                                                          new WrappedString(str3));
            tuple.It_should_fulfill_IEquatable_standards();
        }
}
Do tych testów pomocna będzie nam Tupla i Checker (2 dodatkowe wrappery).
public class EquatableRelationTuple<T> : Tuple<T, T, T>, IStandardEquatable
        where T : IEquatable<T>
{
        private readonly EquatableRelationChecker<T> checker;
 
        public EquatableRelationTuple(T item1, T item2, T item3)
            : base(item1, item2, item3)
        {
            checker = new EquatableRelationChecker<T>(item1, item2, item3);
        }
 
        public void It_should_fulfill_IEquatable_standards()
        {
            checker.It_should_fulfill_IEquatable_standards();
        }
}

 public class EquatableRelationChecker<T> : EquatableRelation<T>
        where T : IEquatable<T>
{
        private readonly T _vakue1;
        private readonly T _value2;
        private readonly T _value3;
 
        public EquatableRelationChecker(T value1, T value2, T value3)
        {
            _vakue1 = value1;
            _value2 = value2;
            _value3 = value3;
        }
 
        public override T GetItemX()
        {
            return _vakue1;
        }
 
        public override T GetItemY()
        {
            return _value2;
        }
 
        public override T GetItemZ()
        {
            return _value3;
        }
}


poniedziałek, 30 września 2013

Kiedy pisać Unit Test?

TDD jest bardzo popularnym podejściem do pisania kodu. Najpierw piszesz test sprawdzający funkcjonalność, który nie przechodzi, później implementujesz ta funkcjonalność, aby ten test mógł przejść, a na końcu refaktorujesz kod i od nowa zaczynasz pisać kolejny test.

Dużo jest przeciwników TDD, gdyż uznaje się, że TDD jest zbyt wolny, nie nadaje się do małych zmian i nie ma czasu na 'ekperymentowanie' z TDD. Nie jestem w tym ekspertem, ale jedno wiem. Pisanie kodu produkcyjnego z TDD nie jest wolniejsze od samego pisania kodu, ale pisanie kodu eksperymentalnego z TDD jest wolniejsze od samego pisania kodu. Jest to najprostszy argument, przeciwko TDD.

Innym podejściem do pisania testów jest zasada Test-First. Kiedyś byłem, na praktyce w zagranicznej firmie, w której dwóch doświadczonych programistów pisało kod z takim podejściem. Jedna osoba pisała testy, a druga osoba implementowała te testy. Wszystko ok, ale bardzo często implementator kodu przychodził do twórcy testów, aby zmienić testy - w trakcie rozwoju aplikacji funkcjonalność się zmieniała jak i pomysły rozwiązania problemów. Test-First bardzo dobrze uzupełnia się z TDD, gdyż jedna osoba (na ogół jest to architekt) pisze podstawowe testy (kontrakty), a implementator kodu dodaje bardziej szczegółowe testy.

Ale, skoro TDD zawiera bardzo dużo czasu, a testy pisane metoda Test-First są często zmieniane to może pisać testy metoda Test-After? Minusem Test-After jest to, że bardzo trudno jest uzyskać wysokie pokrycie kodu. Mamy wtedy wewnętrzną potrzebę, aby zmienić coś w kodzie, aby łatwiej można było napisać test, nie mając przy tym gwarancji, że po zmianie wszystko będzie działało. Na dodatek nigdy nie ma czasu na testy i wszyscy mówią (managery), że po ważniejszych zmianach będzie można przetestować funkcjonalność. Oczywiście po tych zmianach nie mamy pewności czy wszystko działa tak jak wcześniej działało.

Może pisanie testów nie powinno być z góry określone, kiedy należy pisać. Może wystarczą testy, kiedy mamy taką potrzebę - Test-Whenever. Coś takiego jak zasada 80-20. 20% testów sprawdza 80% funkcjonalność. Niektóra funkcjonalność nie potrzebuje testów, a przy kodzie, gdzie mniej bezpieczniej się czujemy to możemy napisać więcej testów. I ta metoda ma swoją wadę. Zawsze jak zaczynam pisać nowy kod to mam wrażenie jakby wszystko było czytelne, oczywiste, proste i wszyscy powinni zrozumieć co kod robi, ale jak po roku wracam do swojego kodu to klnę na autora tego spaghetti kodu :)

Może testy nie powinny wyjść z potrzeby autora code, ale od osoby, która robi code review.

Dylematy z pisaniem testów jest bardzo dużo. Każda metoda ma swoich zwolenników i przeciwników. Może lepiej będzie zastosować metodę Test-Never?

sobota, 6 kwietnia 2013

Architekt jest jak ogrodnik

Bardzo mi się podoba metafora "architekt jest jak ogrodnik". Ogrodnik powinien pielęgnować ogród. Architekt powinien pielęgnować architekturę.

Problem może pojawić się, kiedy mamy architekta, który mówi, że wszystko jest ok, nie trzeba modernizować starego systemu, manualne czynności nie muszą być zautomatyzowane, projekt działa wystarczająco szybko, nie jest potrzebna reużywalność i tak dalej... To tak samo by było, gdyby ogrodnik nie chciałby wyciąć drzewa, które zagraża życiu ludzkiemu, przesadzić suche i brzydkie kwiatki czy podciąć zarośnięty żywopłot i nie dba o dróżki prowadzące przez ogród.

Z drugiej strony mamy architekta, który od nowa chce stworzyć system, zastosuje technologie jakie są teraz modne, a stary system z przyjemnością by usunął. Tak samo jest z ogrodnikiem, który chce wyciąć wszystko i od nowa posadzić nowe roślinki (oczywiście ogrodnik ten nie zna do końca wymagań wszystkich rośliny). Można zrobić fantastyczne ogrody tak jak labirynt znajdujący się w Kurozwękach czy ogród Herrenhäuser w Hannoverze, ale koszt utrzymania takiego ogrodu jest bardzo duży.


Pamiętaj, "architekt jest jak ogrodnik, pielęgnuje architekturę systemu".

piątek, 5 kwietnia 2013

Najlepsze praktyki dla System.Collections.Generic

Krótko chciałem napisać 13 najlepszych praktyk przy używaniu generycznych kolekcji.

1) Nie używaj generyków jeżeli wiesz, że nie potrzebujesz
2) Używaj Stack<T> do implementacji list LIFO
3) Używaj Queue<T> do implementacji list FIFO
4) Używaj List<T> do implementacji listy z losowym dostępem do danych
5) Używaj LinkedList<T> jeżeli potrzebujesz dodawanie lub usuwanie elementów na brzegach listy
6) Używaj HashSet<T> jeżeli lista nie ma duplikatów lub potrzebujesz szybki zbiór danych
7) Używaj foreach na kolekcji kluczy zamiast używać pętli for po IDictionary<TKey,TValue>
8) Nie używaj metody ElementAt() czy ElementAtOrDefault()
9) Używaj dedykowanej klasy zamiast Tupli z dużą ilością parametrów.
10) Używaj SortedDictionary<TKey,TValue> jeżeli potrzebujesz mieć posortowane elementy
11) Używaj SortedSet<T> jeżeli potrzebujesz posortowany zbiór danych
12) Używaj KeyValuePair<TKey,TValue> zamiast Tuple<T1,T2>
13) Jeżeli implementujesz po interfejsie IEnumerable<T> to również zaimplementuj IEnumerable(z powodu wstecznej kompatybilności)