Describe "Measure-Object" -Tags "CI" {
    $testObject = 1,3,4

    It "Should be able to be called without error" {
	{ Measure-Object | Out-Null } | Should Not Throw
    }

    It "Should be able to call on piped input" {
	{ $testObject | Measure-Object } | Should Not Throw
    }

    It "Should be able to count the number of objects input to it" {
	$($testObject | Measure-Object).Count | Should Be $testObject.Length
    }

    It "Should be able to count using the Property switch" {
	$expected = $(Get-ChildItem $TestDrive).Length
	$actual   = $(Get-ChildItem $TestDrive | Measure-Object -Property Length).Count

	$actual | Should Be $expected
    }

    It "Should be able to use wildcards for the Property argument" {
        $data = [pscustomobject]@{ A1 = 1; A2 = 2; C3 = 3 },
                [pscustomobject]@{ A1 = 1; A2 = 2; A3 = 3 }
        $actual = $data | Measure-Object -Property A* -Sum
        $actual.Count | Should Be 3
        $actual[0].Property | Should Be A1
        $actual[0].Sum | Should Be 2
        $actual[0].Count | Should Be 2
        $actual[1].Property | Should Be A2
        $actual[1].Sum | Should Be 4
        $actual[1].Count | Should Be 2
        $actual[2].Property | Should Be A3
        $actual[2].Sum | Should Be 3
        $actual[2].Count | Should Be 1
    }

    Context "Numeric tests" {
	It "Should be able to sum" {
	    $actual   = $testObject | Measure-Object -Sum
	    $expected = 0

	    foreach ( $obj in $testObject )
	    {
		$expected += $obj
	    }

	    $actual.Sum | Should Be $expected
	}

	It "Should be able to average" {
	    $actual   = $testObject | Measure-Object -Average
	    $expected = 0

	    foreach ( $obj in $testObject )
	    {
		$expected += $obj
	    }

	    $expected /= $testObject.length

	    $actual.Average | Should Be $expected
	}

	It "Should be able to return a minimum" {
	    $actual   = $testObject | Measure-Object -Minimum
	    $expected = $testObject[0]

	    for ($i=0; $i -lt $testObject.length; $i++)
	    {
		if ( $testObject[$i] -lt $expected )
		{

		    $expected = $testObject[$i]
		}
	    }

	    $actual.Minimum | Should Be $expected
	}

	It "Should be able to return a minimum when multiple objects are the minimum" {
	    $testMinimum = 1,1,2,4
	    $actual      = $testMinimum | Measure-Object -Minimum
	    $expected    = $testMinimum[0]

	    for ($i=1; $i -lt $testMinimum.length; $i++)
	    {
		if ( $testMinimum[$i] -lt $expected )
		{

		    $expected = $testMinimum[$i]
		}
	    }

	    $actual.Minimum | Should Be $expected
	}

	It "Should be able to return a maximum" {
	    $actual   = $testObject | Measure-Object -Maximum
	    $expected = $testObject[0]

	    for ($i=1; $i -lt $testObject.length; $i++)
	    {
		if ( $testObject[$i] -gt $expected )
		{

		    $expected = $testObject[$i]
		}
	    }

	    $actual.Maximum | Should Be $expected
	}

	It "Should be able to return a maximum when multiple objects are the maximum" {
	    $testMaximum = 1,3,5,5
	    $actual      = $testMaximum | Measure-Object -Maximum
	    $expected    = $testMaximum[0]

	    for ($i=1; $i -lt $testMaximum.length; $i++)
	    {
		if ( $testMaximum[$i] -gt $expected )
		{

		    $expected = $testMaximum[$i]
		}
	    }

	    $actual.Maximum | Should Be $expected
	}
    }

    Context "String tests" {
	$nl = [Environment]::NewLine

	$testString = "HAD I the heavens' embroidered cloths,$nl Enwrought with golden and silver light,$nl The blue and the dim and the dark cloths$nl Of night and light and the half light,$nl I would spread the cloths under your feet:$nl But I, being poor, have only my dreams;$nl I have spread my dreams under your feet;$nl Tread softly because you tread on my dreams."

	It "Should be able to count the number of words in a string" {
	    $expectedLength = $testString.Replace($nl,"").Split().length
	    $actualLength   = $testString | Measure-Object -Word

	    $actualLength.Words | Should Be $expectedLength
	}

	It "Should be able to count the number of characters in a string" {
	    $expectedLength = $testString.length
	    $actualLength   = $testString | Measure-Object -Character

	    $actualLength.Characters | Should Be $expectedLength
	}

	It "Should be able to count the number of lines in a string" {
	    $expectedLength = $testString.Split($nl, [System.StringSplitOptions]::RemoveEmptyEntries).length
	    $actualLength   = $testString | Measure-Object -Line

	    $actualLength.Lines | Should Be $expectedLength
	}
    }
}

Describe "Measure-Object DRT basic functionality" -Tags "CI" {

	BeforeAll {
		if(-not ([System.Management.Automation.PSTypeName]'TestMeasureGeneric').Type)
		{
			Add-Type -TypeDefinition @"
    [System.Flags]
    public enum TestMeasureGeneric : uint
    {
        TestSum = 1,
        TestAverage = 2,
        TestMax = 4,
        TestMin = 8
    }
"@
		}
		if(-not ([System.Management.Automation.PSTypeName]'TestMeasureText').Type)
		{
			Add-Type -TypeDefinition @"
    [System.Flags]
    public enum TestMeasureText : uint
    {
        TestIgnoreWS = 1,
        TestCharacter = 2,
        TestWord = 4,
        TestLine = 8
    }
"@
	}
		$employees = [pscustomobject]@{"FirstName"="joseph"; "LastName"="smith"; "YearsInMS"=15},
                            [pscustomobject]@{"FirstName"="paul"; "LastName"="smith"; "YearsInMS"=15},
                            [pscustomobject]@{"FirstName"="mary jo"; "LastName"="soe"; "YearsInMS"=5},
                            [pscustomobject]@{"FirstName"="edmund`todd `n"; "LastName"="bush"; "YearsInMS"=9}
	}

    It "Measure-Object with Generic enum value options combination should work"{
        $flags = [TestMeasureGeneric]0
		$property = "FirstName"
		$testSum = ($flags -band [TestMeasureGeneric]::TestSum) -gt 0
        $testAverage = ($flags -band [TestMeasureGeneric]::TestAverage) -gt 0
        $testMax = ($flags -band [TestMeasureGeneric]::TestMax) -gt 0
        $testMin = ($flags -band [TestMeasureGeneric]::TestMin) -gt 0
		$result = $employees | Measure-Object -Sum:$testSum -Average:$testAverage -Max:$testMax -Min:$testMin -Prop $property
		$result.Count | Should Be 4
		$result.Sum | Should BeNullOrEmpty
		$result.Average | Should BeNullOrEmpty
		$result.Max | Should BeNullOrEmpty
		$result.Min | Should BeNullOrEmpty
		for ($i = 1; $i -lt 8 * 2; $i++)
        {
			$flags = [TestMeasureGeneric]$i
			$property = "YearsInMS"
			$testSum = ($flags -band [TestMeasureGeneric]::TestSum) -gt 0
			$testAverage = ($flags -band [TestMeasureGeneric]::TestAverage) -gt 0
			$testMax = ($flags -band [TestMeasureGeneric]::TestMax) -gt 0
			$testMin = ($flags -band [TestMeasureGeneric]::TestMin) -gt 0
			$result = $employees | Measure-Object -Sum:$testSum -Average:$testAverage -Max:$testMax -Min:$testMin -Prop $property
			$result.Count | Should Be 4
			if($testSum)
			{
				$result.Sum | Should Be 44
			}
			else
			{
				$result.Sum | Should BeNullOrEmpty
			}

			if($testAverage)
			{
				$result.Average | Should Be 11
			}
			else
			{
				$result.Average | Should BeNullOrEmpty
			}

			if($testMax)
			{
				$result.Maximum | Should Be 15
			}
			else
			{
				$result.Maximum | Should BeNullOrEmpty
			}

			if($testMin)
			{
				$result.Minimum | Should Be 5
			}
			else
			{
				$result.Minimum | Should BeNullOrEmpty
			}
		}
    }

	It "Measure-Object with Text combination should work"{
		for ($i = 1; $i -lt 8 * 2; $i++)
        {
			$flags = [TestMeasureText]$i
			$property = "FirstName"
			$testIgnoreWS = ($flags -band [TestMeasureText]::TestIgnoreWS) -gt 0
			$testCharacter = ($flags -band [TestMeasureText]::TestCharacter) -gt 0
			$testWord = ($flags -band [TestMeasureText]::TestWord) -gt 0
			$testLine = ($flags -band [TestMeasureText]::TestLine) -gt 0
			$result = $employees | Measure-Object -IgnoreWhiteSpace:$testIgnoreWS -Character:$testCharacter -Word:$testWord -Line:$testLine -Prop $property

			if($testCharacter)
			{
				if($testIgnoreWS)
				{
					$result.Characters | Should Be 25
				}
				else
				{
					$result.Characters | Should Be 29
				}
			}
			else
			{
				$result.Characters | Should BeNullOrEmpty
			}

			if($testWord)
			{
				$result.Words | Should Be 6
			}
			else
			{
				$result.Words | Should BeNullOrEmpty
			}

			if($testLine)
			{
				$result.Lines | Should Be 4
			}
			else
			{
				$result.Lines | Should BeNullOrEmpty
			}
		}
    }

	It "Measure-Object with multiple lines should work"{
		$result = "123`n4"|measure-object -line
		$result.Lines | Should Be 2
	}
}
