Merge branch 'master' into aleader/notifications-registry

This commit is contained in:
Andrew Leader 2020-10-28 21:02:13 -06:00
Родитель 0288c06dda b353c78252
Коммит 62bc118f23
11 изменённых файлов: 383 добавлений и 344 удалений

Просмотреть файл

@ -13,14 +13,14 @@ namespace Microsoft.Toolkit.HighPerformance.Streams
internal static partial class MemoryStream
{
/// <summary>
/// Validates the <see cref="Stream.Position"/> argument.
/// Validates the <see cref="Stream.Position"/> argument (it needs to be in the [0, length]) range.
/// </summary>
/// <param name="position">The new <see cref="Stream.Position"/> value being set.</param>
/// <param name="length">The maximum length of the target <see cref="Stream"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ValidatePosition(long position, int length)
{
if ((ulong)position >= (ulong)length)
if ((ulong)position > (ulong)length)
{
ThrowArgumentOutOfRangeExceptionForPosition();
}

Просмотреть файл

@ -1,4 +1,6 @@
# How to add new samples
For the latest info, [visit the wiki article here](https://github.com/windows-toolkit/WindowsCommunityToolkit/wiki/Sample-Development).
# How to add new samples
This document describes how to add a new sample page for a new control you want to add to the toolkit.

Просмотреть файл

@ -3,8 +3,10 @@
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
@ -76,6 +78,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
Collapsed?.Invoke(this, args);
}
/// <summary>
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
/// </summary>
/// <returns>An automation peer for this <see cref="Expander"/>.</returns>
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ExpanderAutomationPeer(this);
}
private void ExpanderToggleButtonPart_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key != VirtualKey.Enter)

Просмотреть файл

@ -0,0 +1,116 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.Toolkit.Uwp.UI.Controls;
using Windows.UI.Xaml.Automation;
using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Automation.Provider;
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
{
/// <summary>
/// Defines a framework element automation peer for the <see cref="Expander"/> control.
/// </summary>
public class ExpanderAutomationPeer : FrameworkElementAutomationPeer, IToggleProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="ExpanderAutomationPeer"/> class.
/// </summary>
/// <param name="owner">
/// The <see cref="Expander" /> that is associated with this <see cref="T:Windows.UI.Xaml.Automation.Peers.ExpanderAutomationPeer" />.
/// </param>
public ExpanderAutomationPeer(Expander owner)
: base(owner)
{
}
/// <summary>Gets the toggle state of the control.</summary>
/// <returns>The toggle state of the control, as a value of the enumeration.</returns>
public ToggleState ToggleState => OwningExpander.IsExpanded ? ToggleState.On : ToggleState.Off;
private Expander OwningExpander
{
get
{
return Owner as Expander;
}
}
/// <summary>Cycles through the toggle states of a control.</summary>
public void Toggle()
{
if (!IsEnabled())
{
throw new ElementNotEnabledException();
}
OwningExpander.IsExpanded = !OwningExpander.IsExpanded;
}
/// <summary>
/// Gets the control type for the element that is associated with the UI Automation peer.
/// </summary>
/// <returns>The control type.</returns>
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Custom;
}
/// <summary>
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
/// differentiates the control represented by this AutomationPeer.
/// </summary>
/// <returns>The string that contains the name.</returns>
protected override string GetClassNameCore()
{
return Owner.GetType().Name;
}
/// <summary>
/// Called by GetName.
/// </summary>
/// <returns>
/// Returns the first of these that is not null or empty:
/// - Value returned by the base implementation
/// - Name of the owning Expander
/// - Expander class name
/// </returns>
protected override string GetNameCore()
{
string name = base.GetNameCore();
if (!string.IsNullOrEmpty(name))
{
return name;
}
if (this.OwningExpander != null)
{
name = this.OwningExpander.Name;
}
if (string.IsNullOrEmpty(name))
{
name = this.GetClassName();
}
return name;
}
/// <summary>
/// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface.
/// </summary>
/// <param name="patternInterface">A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration.</param>
/// <returns>The object that supports the specified pattern, or null if unsupported.</returns>
protected override object GetPatternCore(PatternInterface patternInterface)
{
switch (patternInterface)
{
case PatternInterface.Toggle:
return this;
}
return base.GetPatternCore(patternInterface);
}
}
}

Просмотреть файл

@ -2,11 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Drawing;
using Microsoft.Toolkit.Diagnostics;
namespace Microsoft.Toolkit.Uwp.UI.Controls
{
@ -16,23 +14,81 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
/// <see cref="UniformGrid.GetFreeSpot"/> iterator.
/// This is used so we can better isolate our logic and make it easier to test.
/// </summary>
internal class TakenSpotsReferenceHolder
internal sealed class TakenSpotsReferenceHolder
{
/// <summary>
/// Gets or sets the array to hold taken spots.
/// True value indicates an item in the layout is fixed to that position.
/// False values indicate free openings where an item can be placed.
/// The <see cref="BitArray"/> instance used to efficiently track empty spots.
/// </summary>
public bool[,] SpotsTaken { get; set; }
private readonly BitArray spotsTaken;
/// <summary>
/// Initializes a new instance of the <see cref="TakenSpotsReferenceHolder"/> class.
/// </summary>
/// <param name="rows">The number of rows to track.</param>
/// <param name="columns">The number of columns to track.</param>
public TakenSpotsReferenceHolder(int rows, int columns)
{
SpotsTaken = new bool[rows, columns];
Guard.IsGreaterThanOrEqualTo(rows, 0, nameof(rows));
Guard.IsGreaterThanOrEqualTo(columns, 0, nameof(columns));
Height = rows;
Width = columns;
this.spotsTaken = new BitArray(rows * columns);
}
public TakenSpotsReferenceHolder(bool[,] array)
/// <summary>
/// Gets the height of the grid to monitor.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets the width of the grid to monitor.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets or sets the value of a specified grid cell.
/// </summary>
/// <param name="i">The vertical offset.</param>
/// <param name="j">The horizontal offset.</param>
public bool this[int i, int j]
{
SpotsTaken = array;
get => this.spotsTaken[(i * Width) + j];
set => this.spotsTaken[(i * Width) + j] = value;
}
/// <summary>
/// Fills the specified area in the current grid with a given value.
/// If invalid coordinates are given, they will simply be ignored and no exception will be thrown.
/// </summary>
/// <param name="value">The value to fill the target area with.</param>
/// <param name="row">The row to start on (inclusive, 0-based index).</param>
/// <param name="column">The column to start on (inclusive, 0-based index).</param>
/// <param name="width">The positive width of area to fill.</param>
/// <param name="height">The positive height of area to fill.</param>
public void Fill(bool value, int row, int column, int width, int height)
{
Rectangle bounds = new Rectangle(0, 0, Width, Height);
// Precompute bounds to skip branching in main loop
bounds.Intersect(new Rectangle(column, row, width, height));
for (int i = bounds.Top; i < bounds.Bottom; i++)
{
for (int j = bounds.Left; j < bounds.Right; j++)
{
this[i, j] = value;
}
}
}
/// <summary>
/// Resets the current reference holder.
/// </summary>
public void Reset()
{
this.spotsTaken.SetAll(false);
}
}
}

Просмотреть файл

@ -22,16 +22,16 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
{
if (topdown)
{
var rows = arrayref.SpotsTaken.GetLength(0);
var rows = arrayref.Height;
// Layout spots from Top-Bottom, Left-Right (right-left handled automatically by Grid with Flow-Direction).
// Effectively transpose the Grid Layout.
for (int c = 0; c < arrayref.SpotsTaken.GetLength(1); c++)
for (int c = 0; c < arrayref.Width; c++)
{
int start = (c == 0 && firstcolumn > 0 && firstcolumn < rows) ? firstcolumn : 0;
for (int r = start; r < rows; r++)
{
if (!arrayref.SpotsTaken[r, c])
if (!arrayref[r, c])
{
yield return (r, c);
}
@ -40,17 +40,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
}
else
{
var columns = arrayref.SpotsTaken.GetLength(1);
var columns = arrayref.Width;
// Layout spots as normal from Left-Right.
// (right-left handled automatically by Grid with Flow-Direction
// during its layout, internal model is always left-right).
for (int r = 0; r < arrayref.SpotsTaken.GetLength(0); r++)
for (int r = 0; r < arrayref.Height; r++)
{
int start = (r == 0 && firstcolumn > 0 && firstcolumn < columns) ? firstcolumn : 0;
for (int c = start; c < columns; c++)
{
if (!arrayref.SpotsTaken[r, c])
if (!arrayref[r, c])
{
yield return (r, c);
}

Просмотреть файл

@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Toolkit.Extensions;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@ -20,6 +19,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
// Internal list we use to keep track of items that we don't have space to layout.
private List<UIElement> _overflow = new List<UIElement>();
/// <summary>
/// The <see cref="TakenSpotsReferenceHolder"/> instance in use, if any.
/// </summary>
private TakenSpotsReferenceHolder _spotref;
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
@ -36,7 +40,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
SetupRowDefinitions(rows);
SetupColumnDefinitions(columns);
var spotref = new TakenSpotsReferenceHolder(rows, columns);
TakenSpotsReferenceHolder spotref;
// If the last spot holder matches the size currently in use, just reset
// that instance and reuse it to avoid allocating a new bit array.
if (_spotref != null && _spotref.Height == rows && _spotref.Width == columns)
{
spotref = _spotref;
spotref.Reset();
}
else
{
spotref = _spotref = new TakenSpotsReferenceHolder(rows, columns);
}
// Figure out which children we should automatically layout and where available openings are.
foreach (var child in visible)
@ -56,7 +73,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
else
{
SetAutoLayout(child, false);
spotref.SpotsTaken.Fill(true, row, col, colspan, rowspan); // row, col, width, height
spotref.Fill(true, row, col, colspan, rowspan);
}
}
@ -100,7 +118,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
if (rowspan > 1 || colspan > 1)
{
// TODO: Need to tie this into iterator
spotref.SpotsTaken.Fill(true, row, column, GetColumnSpan(child), GetRowSpan(child)); // row, col, width, height
spotref.Fill(true, row, column, colspan, rowspan);
}
}
else
@ -135,7 +153,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
}
// Return our desired size based on the largest child we found, our dimensions, and spacing.
var desiredSize = new Size((maxWidth * (double)columns) + columnSpacingSize, (maxHeight * (double)rows) + rowSpacingSize);
var desiredSize = new Size((maxWidth * columns) + columnSpacingSize, (maxHeight * rows) + rowSpacingSize);
// Required to perform regular grid measurement, but ignore result.
base.MeasureOverride(desiredSize);

Просмотреть файл

@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Toolkit.Extensions
{
@ -13,71 +14,6 @@ namespace Microsoft.Toolkit.Extensions
/// </summary>
public static class ArrayExtensions
{
/// <summary>
/// Fills elements of a rectangular array at the given position and size to a specific value.
/// Ranges given will fill in as many elements as possible, ignoring positions outside the bounds of the array.
/// </summary>
/// <typeparam name="T">The element type of the array.</typeparam>
/// <param name="array">The source array.</param>
/// <param name="value">Value to fill with.</param>
/// <param name="row">Row to start on (inclusive, zero-index).</param>
/// <param name="col">Column to start on (inclusive, zero-index).</param>
/// <param name="width">Positive width of area to fill.</param>
/// <param name="height">Positive height of area to fill.</param>
public static void Fill<T>(this T[,] array, T value, int row, int col, int width, int height)
{
for (int r = row; r < row + height; r++)
{
for (int c = col; c < col + width; c++)
{
if (r >= 0 && c >= 0 && r < array.GetLength(0) && c < array.GetLength(1))
{
array[r, c] = value;
}
}
}
}
/// <summary>
/// Yields a row from a rectangular array.
/// </summary>
/// <typeparam name="T">The element type of the array.</typeparam>
/// <param name="rectarray">The source array.</param>
/// <param name="row">Row record to retrieve, 0-based index.</param>
/// <returns>Yielded row.</returns>
public static IEnumerable<T> GetRow<T>(this T[,] rectarray, int row)
{
if (row < 0 || row >= rectarray.GetLength(0))
{
throw new ArgumentOutOfRangeException(nameof(row));
}
for (int c = 0; c < rectarray.GetLength(1); c++)
{
yield return rectarray[row, c];
}
}
/// <summary>
/// Yields a column from a rectangular array.
/// </summary>
/// <typeparam name="T">The element type of the array.</typeparam>
/// <param name="rectarray">The source array.</param>
/// <param name="column">Column record to retrieve, 0-based index.</param>
/// <returns>Yielded column.</returns>
public static IEnumerable<T> GetColumn<T>(this T[,] rectarray, int column)
{
if (column < 0 || column >= rectarray.GetLength(1))
{
throw new ArgumentOutOfRangeException(nameof(column));
}
for (int r = 0; r < rectarray.GetLength(0); r++)
{
yield return rectarray[r, column];
}
}
/// <summary>
/// Yields a column from a jagged array.
/// An exception will be thrown if the column is out of bounds, and return default in places where there are no elements from inner arrays.
@ -98,7 +34,7 @@ namespace Microsoft.Toolkit.Extensions
{
if (column >= rectarray[r].Length)
{
yield return default(T);
yield return default;
continue;
}
@ -112,10 +48,28 @@ namespace Microsoft.Toolkit.Extensions
/// </summary>
/// <typeparam name="T">The element type of the array.</typeparam>
/// <param name="array">The source array.</param>
/// <returns>String representation of the array.</returns>
/// <returns>The <see cref="string"/> representation of the array.</returns>
public static string ToArrayString<T>(this T[] array)
{
return "[" + string.Join(",\t", array) + "]";
// The returned string will be in the following format:
// [1, 2, 3]
StringBuilder builder = new StringBuilder();
builder.Append('[');
for (int i = 0; i < array.Length; i++)
{
if (i != 0)
{
builder.Append(",\t");
}
builder.Append(array[i].ToString());
}
builder.Append(']');
return builder.ToString();
}
/// <summary>
@ -126,32 +80,89 @@ namespace Microsoft.Toolkit.Extensions
/// <returns>String representation of the array.</returns>
public static string ToArrayString<T>(this T[][] mdarray)
{
string[] inner = new string[mdarray.GetLength(0)];
// The returned string uses the same format as the overload for 2D arrays
StringBuilder builder = new StringBuilder();
for (int r = 0; r < mdarray.GetLength(0); r++)
builder.Append('[');
for (int i = 0; i < mdarray.Length; i++)
{
inner[r] = string.Join(",\t", mdarray[r]);
if (i != 0)
{
builder.Append(',');
builder.Append(Environment.NewLine);
builder.Append(' ');
}
builder.Append('[');
T[] row = mdarray[i];
for (int j = 0; j < row.Length; j++)
{
if (j != 0)
{
builder.Append(",\t");
}
builder.Append(row[j].ToString());
}
builder.Append(']');
}
return "[[" + string.Join("]," + Environment.NewLine + " [", inner) + "]]";
builder.Append(']');
return builder.ToString();
}
/// <summary>
/// Returns a simple string representation of a rectangular array.
/// Returns a simple string representation of a 2D array.
/// </summary>
/// <typeparam name="T">The element type of the array.</typeparam>
/// <param name="rectarray">The source array.</param>
/// <returns>String representation of the array.</returns>
public static string ToArrayString<T>(this T[,] rectarray)
/// <param name="array">The source array.</param>
/// <returns>The <see cref="string"/> representation of the array.</returns>
public static string ToArrayString<T>(this T[,] array)
{
string[] inner = new string[rectarray.GetLength(0)];
// The returned string will be in the following format:
// [[1, 2, 3],
// [4, 5, 6],
// [7, 8, 9]]
StringBuilder builder = new StringBuilder();
for (int r = 0; r < rectarray.GetLength(0); r++)
builder.Append('[');
int
height = array.GetLength(0),
width = array.GetLength(1);
for (int i = 0; i < height; i++)
{
inner[r] = string.Join(",\t", rectarray.GetRow(r));
if (i != 0)
{
builder.Append(',');
builder.Append(Environment.NewLine);
builder.Append(' ');
}
builder.Append('[');
for (int j = 0; j < width; j++)
{
if (j != 0)
{
builder.Append(",\t");
}
builder.Append(array[i, j].ToString());
}
builder.Append(']');
}
return "[[" + string.Join("]," + Environment.NewLine + " [", inner) + "]]";
builder.Append(']');
return builder.ToString();
}
}
}

Просмотреть файл

@ -78,6 +78,42 @@ namespace UnitTests.HighPerformance.Streams
Assert.AreEqual(stream.Position, 32);
}
// See https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3536
[TestCategory("MemoryStream")]
[TestMethod]
public void Test_MemoryStream_WriteToEndAndRefreshPosition()
{
byte[]
array = new byte[10],
temp = new byte[1];
ReadOnlyMemory<byte> memory = array;
using var stream = memory.AsStream();
for (int i = 0; i < array.Length; i++)
{
int read = stream.Read(temp, 0, 1);
Assert.AreEqual(read, 1);
Assert.AreEqual(stream.Position, i + 1);
}
Assert.AreEqual(stream.Position, array.Length);
// These should not throw, seeking to the end is valid
stream.Position = stream.Position;
Assert.AreEqual(stream.Position, array.Length);
stream.Seek(array.Length, SeekOrigin.Begin);
Assert.AreEqual(stream.Position, array.Length);
stream.Seek(0, SeekOrigin.Current);
Assert.AreEqual(stream.Position, array.Length);
stream.Seek(0, SeekOrigin.End);
Assert.AreEqual(stream.Position, array.Length);
}
[TestCategory("MemoryStream")]
[TestMethod]
public void Test_MemoryStream_ReadWrite_Array()

Просмотреть файл

@ -13,161 +13,6 @@ namespace UnitTests.Extensions
[TestClass]
public class Test_ArrayExtensions
{
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_FillArrayMid()
{
bool[,] test = new bool[4, 5];
test.Fill(true, 1, 1, 3, 2);
var expected = new bool[,]
{
{ false, false, false, false, false },
{ false, true, true, true, false },
{ false, true, true, true, false },
{ false, false, false, false, false },
};
CollectionAssert.AreEqual(
expected,
test,
"Fill failed. Expected:\n{0}.\nActual:\n{1}",
expected.ToArrayString(),
test.ToArrayString());
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_FillArrayTwice()
{
bool[,] test = new bool[4, 5];
test.Fill(true, 0, 0, 1, 2);
test.Fill(true, 1, 3, 2, 2);
var expected = new bool[,]
{
{ true, false, false, false, false },
{ true, false, false, true, true },
{ false, false, false, true, true },
{ false, false, false, false, false },
};
CollectionAssert.AreEqual(
expected,
test,
"Fill failed. Expected:\n{0}.\nActual:\n{1}",
expected.ToArrayString(),
test.ToArrayString());
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_FillArrayNegativeSize()
{
bool[,] test = new bool[4, 5];
test.Fill(true, 3, 4, -3, -2);
// TODO: We may want to think about this pattern in the future:
/*var expected = new bool[,]
{
{ false, false, false, false, false },
{ false, false, false, false, false },
{ false, false, true, true, true },
{ false, false, true, true, true },
};*/
var expected = new bool[,]
{
{ false, false, false, false, false },
{ false, false, false, false, false },
{ false, false, false, false, false },
{ false, false, false, false, false },
};
CollectionAssert.AreEqual(
expected,
test,
"Fill failed. Expected:\n{0}.\nActual:\n{1}",
expected.ToArrayString(),
test.ToArrayString());
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_FillArrayBottomEdgeBoundary()
{
bool[,] test = new bool[4, 5];
test.Fill(true, 1, 2, 2, 4);
var expected = new bool[,]
{
{ false, false, false, false, false },
{ false, false, true, true, false },
{ false, false, true, true, false },
{ false, false, true, true, false },
};
CollectionAssert.AreEqual(
expected,
test,
"Fill failed. Expected:\n{0}.\nActual:\n{1}",
expected.ToArrayString(),
test.ToArrayString());
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_FillArrayTopLeftCornerNegativeBoundary()
{
bool[,] test = new bool[4, 5];
test.Fill(true, -1, -1, 3, 3);
var expected = new bool[,]
{
{ true, true, false, false, false },
{ true, true, false, false, false },
{ false, false, false, false, false },
{ false, false, false, false, false },
};
CollectionAssert.AreEqual(
expected,
test,
"Fill failed. Expected:\n{0}.\nActual:\n{1}",
expected.ToArrayString(),
test.ToArrayString());
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_FillArrayBottomRightCornerBoundary()
{
bool[,] test = new bool[5, 4];
test.Fill(true, 3, 2, 3, 3);
var expected = new bool[,]
{
{ false, false, false, false },
{ false, false, false, false },
{ false, false, false, false },
{ false, false, true, true },
{ false, false, true, true },
};
CollectionAssert.AreEqual(
expected,
test,
"Fill failed. Expected:\n{0}.\nActual:\n{1}",
expected.ToArrayString(),
test.ToArrayString());
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_Jagged_GetColumn()
@ -206,82 +51,6 @@ namespace UnitTests.Extensions
});
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_Rectangular_GetColumn()
{
int[,] array =
{
{ 5, 2, 4 },
{ 6, 3, 9 },
{ 7, -1, 0 }
};
var col = array.GetColumn(1).ToArray();
CollectionAssert.AreEquivalent(new int[] { 2, 3, -1 }, col);
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_Rectangular_GetColumn_Exception()
{
int[,] array =
{
{ 5, 2, 4 },
{ 6, 3, 0 },
{ 7, 0, 0 }
};
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
array.GetColumn(-1).ToArray();
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
array.GetColumn(3).ToArray();
});
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_Rectangular_GetRow()
{
int[,] array =
{
{ 5, 2, 4 },
{ 6, 3, 9 },
{ 7, -1, 0 }
};
var col = array.GetRow(1).ToArray();
CollectionAssert.AreEquivalent(new int[] { 6, 3, 9 }, col);
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_Rectangular_GetRow_Exception()
{
int[,] array =
{
{ 5, 2, 4 },
{ 6, 3, 0 },
{ 7, 0, 0 }
};
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
array.GetRow(-1).ToArray();
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
array.GetRow(3).ToArray();
});
}
[TestCategory("ArrayExtensions")]
[TestMethod]
public void Test_ArrayExtensions_Rectangular_ToString()

Просмотреть файл

@ -13,11 +13,31 @@ namespace UnitTests.UI.Controls
[TestClass]
public class Test_UniformGrid_FreeSpots
{
/// <summary>
/// Creates a <see cref="TakenSpotsReferenceHolder"/> instance with the specified values.
/// </summary>
/// <param name="array">The source array to populate the instance to return.</param>
/// <returns>A <see cref="TakenSpotsReferenceHolder"/> with the given values.</returns>
private static TakenSpotsReferenceHolder CreateTakenSpotsReferenceHolder(bool[,] array)
{
var refHolder = new TakenSpotsReferenceHolder(array.GetLength(0), array.GetLength(1));
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
refHolder[i, j] = array[i, j];
}
}
return refHolder;
}
[TestCategory("UniformGrid")]
[UITestMethod]
public void Test_UniformGrid_GetFreeSpots_Basic()
{
var testRef = new TakenSpotsReferenceHolder(new bool[4, 5]
var testRef = CreateTakenSpotsReferenceHolder(new bool[4, 5]
{
{ false, true, false, true, false },
{ false, true, true, true, false },
@ -47,7 +67,7 @@ namespace UnitTests.UI.Controls
[UITestMethod]
public void Test_UniformGrid_GetFreeSpots_FirstColumn()
{
var testRef = new TakenSpotsReferenceHolder(new bool[4, 5]
var testRef = CreateTakenSpotsReferenceHolder(new bool[4, 5]
{
{ true, false, false, true, false },
{ false, true, true, true, false },
@ -77,7 +97,7 @@ namespace UnitTests.UI.Controls
[UITestMethod]
public void Test_UniformGrid_GetFreeSpots_FirstColumnEndBoundMinusOne()
{
var testRef = new TakenSpotsReferenceHolder(new bool[3, 3]
var testRef = CreateTakenSpotsReferenceHolder(new bool[3, 3]
{
{ false, false, false },
{ false, false, false },
@ -105,7 +125,7 @@ namespace UnitTests.UI.Controls
[UITestMethod]
public void Test_UniformGrid_GetFreeSpots_FirstColumnEndBound()
{
var testRef = new TakenSpotsReferenceHolder(new bool[3, 3]
var testRef = CreateTakenSpotsReferenceHolder(new bool[3, 3]
{
{ false, false, false },
{ false, false, false },
@ -133,7 +153,7 @@ namespace UnitTests.UI.Controls
[UITestMethod]
public void Test_UniformGrid_GetFreeSpots_FirstColumnEndBound_TopDown()
{
var testRef = new TakenSpotsReferenceHolder(new bool[3, 3]
var testRef = CreateTakenSpotsReferenceHolder(new bool[3, 3]
{
{ false, false, false },
{ false, false, false },
@ -161,7 +181,7 @@ namespace UnitTests.UI.Controls
[UITestMethod]
public void Test_UniformGrid_GetFreeSpots_VerticalOrientation()
{
var testRef = new TakenSpotsReferenceHolder(new bool[4, 5]
var testRef = CreateTakenSpotsReferenceHolder(new bool[4, 5]
{
{ false, false, false, true, false },
{ false, true, true, false, false },