forked from Azure/azure-sdk-for-net
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FormRecognizer] Add test coverage to FormLayoutClient (Azure#10921)
- Loading branch information
Showing
7 changed files
with
363 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
148 changes: 148 additions & 0 deletions
148
...ormrecognizer/Azure.AI.FormRecognizer/tests/FormLayoutClient/FormLayoutClientLiveTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Azure.AI.FormRecognizer.Models; | ||
using Azure.Core.Testing; | ||
using NUnit.Framework; | ||
|
||
namespace Azure.AI.FormRecognizer.Tests | ||
{ | ||
/// <summary> | ||
/// The suite of tests for the <see cref="FormLayoutClient"/> class. | ||
/// </summary> | ||
/// <remarks> | ||
/// These tests have a dependency on live Azure services and may incur costs for the associated | ||
/// Azure subscription. | ||
/// </remarks> | ||
[LiveOnly] | ||
public class FormLayoutClientLiveTests : ClientTestBase | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="FormLayoutClientLiveTests"/> class. | ||
/// </summary> | ||
/// <param name="isAsync">A flag used by the Azure Core Test Framework to differentiate between tests for asynchronous and synchronous methods.</param> | ||
public FormLayoutClientLiveTests(bool isAsync) : base(isAsync) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Verifies that the <see cref="FormLayoutClient" /> is able to connect to the Form | ||
/// Recognizer cognitive service and perform operations. | ||
/// </summary> | ||
[Test] | ||
[TestCase(true)] | ||
[TestCase(false, Ignore = "The Invoice_1.pdf hasn't been uploaded to GitHub yet.")] | ||
public async Task StartExtractLayoutsPopulatesExtractedLayoutPage(bool useStream) | ||
{ | ||
var client = CreateInstrumentedClient(); | ||
Operation<IReadOnlyList<ExtractedLayoutPage>> operation; | ||
|
||
if (useStream) | ||
{ | ||
using var stream = new FileStream(TestEnvironment.RetrieveInvoicePath(1), FileMode.Open); | ||
operation = await client.StartExtractLayoutsAsync(stream, ContentType.Jpeg); | ||
} | ||
else | ||
{ | ||
var uri = new Uri(TestEnvironment.RetrieveInvoiceUri(1)); | ||
operation = await client.StartExtractLayoutsAsync(uri); | ||
} | ||
|
||
await operation.WaitForCompletionAsync(); | ||
|
||
Assert.IsTrue(operation.HasValue); | ||
|
||
var layoutPage = operation.Value.Single(); | ||
|
||
// The expected values are based on the values returned by the service, and not the actual | ||
// values present in the form. We are not testing the service here, but the SDK. | ||
|
||
Assert.AreEqual(1, layoutPage.PageNumber); | ||
|
||
var rawPage = layoutPage.RawExtractedPage; | ||
|
||
Assert.AreEqual(1, rawPage.Page); | ||
Assert.AreEqual(LengthUnit.Inch, rawPage.Unit); | ||
Assert.AreEqual(8.5, rawPage.Width); | ||
Assert.AreEqual(11, rawPage.Height); | ||
Assert.AreEqual(0, rawPage.Angle); | ||
Assert.AreEqual(18, rawPage.Lines.Count); | ||
|
||
var lines = rawPage.Lines.ToList(); | ||
|
||
for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) | ||
{ | ||
var line = lines[lineIndex]; | ||
|
||
Assert.NotNull(line.Text, $"Text should not be null in line {lineIndex}."); | ||
Assert.Greater(line.Words.Count, 0, $"There should be at least one word in line {lineIndex}."); | ||
Assert.AreEqual(4, line.BoundingBox.Points.Count(), $"There should be exactly 4 points in the bounding box in line {lineIndex}."); | ||
} | ||
|
||
var table = layoutPage.Tables.Single(); | ||
|
||
Assert.AreEqual(2, table.RowCount); | ||
Assert.AreEqual(6, table.ColumnCount); | ||
|
||
var cells = table.Cells.ToList(); | ||
|
||
Assert.AreEqual(10, cells.Count); | ||
|
||
var expectedText = new string[2, 6] | ||
{ | ||
{ "Invoice Number", "Invoice Date", "Invoice Due Date", "Charges", "", "VAT ID" }, | ||
{ "34278587", "6/18/2017", "6/24/2017", "$56,651.49", "", "PT" } | ||
}; | ||
|
||
foreach (var cell in cells) | ||
{ | ||
Assert.GreaterOrEqual(cell.RowIndex, 0, $"Cell with text {cell.Text} should have row index greater than or equal to zero."); | ||
Assert.Less(cell.RowIndex, table.RowCount, $"Cell with text {cell.Text} should have row index less than {table.RowCount}."); | ||
Assert.GreaterOrEqual(cell.ColumnIndex, 0, $"Cell with text {cell.Text} should have column index greater than or equal to zero."); | ||
Assert.Less(cell.ColumnIndex, table.ColumnCount, $"Cell with text {cell.Text} should have column index less than {table.ColumnCount}."); | ||
|
||
// There's a single cell in the table (row = 1, column = 3) that has a column span of 2. | ||
|
||
var expectedColumnSpan = (cell.RowIndex == 1 && cell.ColumnIndex == 3) ? 2 : 1; | ||
|
||
Assert.AreEqual(1, cell.RowSpan, $"Cell with text {cell.Text} should have a row span of 1."); | ||
Assert.AreEqual(expectedColumnSpan, cell.ColumnSpan, $"Cell with text {cell.Text} should have a column span of {expectedColumnSpan}."); | ||
|
||
Assert.AreEqual(expectedText[cell.RowIndex, cell.ColumnIndex], cell.Text); | ||
|
||
Assert.IsFalse(cell.IsFooter, $"Cell with text {cell.Text} should not have been classified as footer."); | ||
Assert.IsFalse(cell.IsHeader, $"Cell with text {cell.Text} should not have been classified as header."); | ||
|
||
Assert.GreaterOrEqual(cell.Confidence, 0, $"Cell with text {cell.Text} should have confidence greater than or equal to zero."); | ||
Assert.LessOrEqual(cell.RowIndex, 1, $"Cell with text {cell.Text} should have confidence less than or equal to one."); | ||
|
||
Assert.Greater(cell.RawExtractedItems.Count, 0, $"Cell with text {cell.Text} should have at least one extracted item."); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Creates a <see cref="FormLayoutClient" /> with the endpoint and API key provided via environment | ||
/// variables and instruments it to make use of the Azure Core Test Framework functionalities. | ||
/// </summary> | ||
/// <returns>The instrumented <see cref="FormLayoutClient" />.</returns> | ||
private FormLayoutClient CreateInstrumentedClient() | ||
{ | ||
var endpointEnvironmentVariable = Environment.GetEnvironmentVariable(TestEnvironment.EndpointEnvironmentVariableName); | ||
var keyEnvironmentVariable = Environment.GetEnvironmentVariable(TestEnvironment.ApiKeyEnvironmentVariableName); | ||
|
||
Assert.NotNull(endpointEnvironmentVariable); | ||
Assert.NotNull(keyEnvironmentVariable); | ||
|
||
var endpoint = new Uri(endpointEnvironmentVariable); | ||
var credential = new FormRecognizerApiKeyCredential(keyEnvironmentVariable); | ||
var client = new FormLayoutClient(endpoint, credential); | ||
|
||
return InstrumentClient(client); | ||
} | ||
} | ||
} |
136 changes: 136 additions & 0 deletions
136
sdk/formrecognizer/Azure.AI.FormRecognizer/tests/FormLayoutClient/FormLayoutClientTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Azure.AI.FormRecognizer.Models; | ||
using Azure.Core.Testing; | ||
using NUnit.Framework; | ||
|
||
namespace Azure.AI.FormRecognizer.Tests | ||
{ | ||
/// <summary> | ||
/// The suite of tests for the <see cref="FormLayoutClient"/> class. | ||
/// </summary> | ||
public class FormLayoutClientTests : ClientTestBase | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="FormLayoutClientTests"/> class. | ||
/// </summary> | ||
/// <param name="isAsync">A flag used by the Azure Core Test Framework to differentiate between tests for asynchronous and synchronous methods.</param> | ||
public FormLayoutClientTests(bool isAsync) : base(isAsync) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Verifies functionality of the <see cref="FormLayoutClient"/> constructors. | ||
/// </summary> | ||
[Test] | ||
[Ignore("Argument validation not implemented yet.")] | ||
public void ConstructorRequiresTheEndpoint() | ||
{ | ||
var credential = new FormRecognizerApiKeyCredential("key"); | ||
|
||
Assert.Throws<ArgumentNullException>(() => new FormLayoutClient(null, credential)); | ||
Assert.Throws<ArgumentNullException>(() => new FormLayoutClient(null, credential, new FormRecognizerClientOptions())); | ||
} | ||
|
||
/// <summary> | ||
/// Verifies functionality of the <see cref="FormLayoutClient"/> constructors. | ||
/// </summary> | ||
[Test] | ||
[Ignore("Argument validation not implemented yet.")] | ||
public void ConstructorRequiresTheCredential() | ||
{ | ||
var endpoint = new Uri("http://localhost"); | ||
|
||
Assert.Throws<ArgumentNullException>(() => new FormLayoutClient(endpoint, null)); | ||
Assert.Throws<ArgumentNullException>(() => new FormLayoutClient(endpoint, null, new FormRecognizerClientOptions())); | ||
} | ||
|
||
/// <summary> | ||
/// Verifies functionality of the <see cref="FormLayoutClient"/> constructors. | ||
/// </summary> | ||
[Test] | ||
[Ignore("Argument validation not implemented yet.")] | ||
public void ConstructorRequiresTheOptions() | ||
{ | ||
var endpoint = new Uri("http://localhost"); | ||
var credential = new FormRecognizerApiKeyCredential("key"); | ||
|
||
Assert.Throws<ArgumentNullException>(() => new FormLayoutClient(endpoint, credential, null)); | ||
} | ||
|
||
/// <summary> | ||
/// Verifies functionality of the <see cref="FormLayoutClient.StartExtractLayoutsAsync(Stream, ContentType, CancellationToken)"/> | ||
/// method. | ||
/// </summary> | ||
[Test] | ||
[Ignore("Argument validation not implemented yet.")] | ||
public void StartExtractLayoutsWithStreamRequiresTheStream() | ||
{ | ||
var client = CreateInstrumentedClient(); | ||
Assert.ThrowsAsync<ArgumentNullException>(async () => await client.StartExtractLayoutsAsync(null, ContentType.Jpeg)); | ||
} | ||
|
||
/// <summary> | ||
/// Verifies functionality of the <see cref="FormLayoutClient.StartExtractLayoutsAsync(Stream, ContentType, CancellationToken)"/> | ||
/// method. | ||
/// </summary> | ||
[Test] | ||
public void StartExtractLayoutsWithStreamRespectsTheCancellationToken() | ||
{ | ||
var client = CreateInstrumentedClient(); | ||
|
||
using var stream = new MemoryStream(Array.Empty<byte>()); | ||
using var cancellationSource = new CancellationTokenSource(); | ||
cancellationSource.Cancel(); | ||
|
||
Assert.ThrowsAsync<TaskCanceledException>(async () => await client.StartExtractLayoutsAsync(stream, ContentType.Jpeg, cancellationSource.Token)); | ||
} | ||
|
||
/// <summary> | ||
/// Verifies functionality of the <see cref="FormLayoutClient.StartExtractLayoutsAsync(Uri, CancellationToken)"/> | ||
/// method. | ||
/// </summary> | ||
[Test] | ||
[Ignore("Argument validation not implemented yet.")] | ||
public void StartExtractLayoutsWithUriRequiresTheUri() | ||
{ | ||
var client = CreateInstrumentedClient(); | ||
Assert.ThrowsAsync<ArgumentNullException>(async () => await client.StartExtractLayoutsAsync(null)); | ||
} | ||
|
||
/// <summary> | ||
/// Verifies functionality of the <see cref="FormLayoutClient.StartExtractLayoutsAsync(Uri, CancellationToken)"/> | ||
/// method. | ||
/// </summary> | ||
[Test] | ||
public void StartExtractReceiptsWithUriRespectsTheCancellationToken() | ||
{ | ||
var client = CreateInstrumentedClient(); | ||
var fakeUri = new Uri("http://localhost"); | ||
|
||
using var cancellationSource = new CancellationTokenSource(); | ||
cancellationSource.Cancel(); | ||
|
||
Assert.ThrowsAsync<TaskCanceledException>(async () => await client.StartExtractLayoutsAsync(fakeUri, cancellationSource.Token)); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a fake <see cref="FormLayoutClient" /> and instruments it to make use of the Azure Core | ||
/// Test Framework functionalities. | ||
/// </summary> | ||
/// <returns>The instrumented <see cref="FormLayoutClient" />.</returns> | ||
private FormLayoutClient CreateInstrumentedClient() | ||
{ | ||
var fakeEndpoint = new Uri("http://localhost"); | ||
var fakeCredential = new FormRecognizerApiKeyCredential("fakeKey"); | ||
var client = new FormLayoutClient(fakeEndpoint, fakeCredential); | ||
|
||
return InstrumentClient(client); | ||
} | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
sdk/formrecognizer/Azure.AI.FormRecognizer/tests/Infrastructure/TestEnvironment.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.IO; | ||
|
||
namespace Azure.AI.FormRecognizer.Tests | ||
{ | ||
/// <summary> | ||
/// A helper class used to retrieve information to be used for tests. | ||
/// </summary> | ||
public static class TestEnvironment | ||
{ | ||
/// <summary>The name of the environment variable from which the Form Recognizer resource's endpoint will be extracted for the live tests.</summary> | ||
public const string EndpointEnvironmentVariableName = "FORM_RECOGNIZER_ENDPOINT"; | ||
|
||
/// <summary>The name of the environment variable from which the Form Recognizer resource's API key will be extracted for the live tests.</summary> | ||
public const string ApiKeyEnvironmentVariableName = "FORM_RECOGNIZER_API_KEY"; | ||
|
||
/// <summary>The name of the folder in which test assets are stored.</summary> | ||
private const string AssetsFolderName = "Assets"; | ||
|
||
/// <summary>The name of the JPG file which contains the receipt to be used for tests.</summary> | ||
private const string ReceiptFilename = "contoso-receipt.jpg"; | ||
|
||
/// <summary>The format to generate the filenames of the PDF forms to be used for tests.</summary> | ||
private const string InvoiceFilenameFormat = "Invoice_{0}.pdf"; | ||
|
||
/// <summary>The format to generate the GitHub URIs of the files to be used for tests.</summary> | ||
private const string FileUriFormat = "https://raw.githubusercontent.com/Azure/azure-sdk-for-net/master/sdk/formrecognizer/Azure.AI.FormRecognizer/tests/{0}/{1}"; | ||
|
||
/// <summary> | ||
/// The relative path to the JPG file which contains the receipt to be used for tests. | ||
/// </summary> | ||
/// <value>The relative path to the JPG file.</value> | ||
public static string ReceiptPath => Path.Combine(AssetsFolderName, ReceiptFilename); | ||
|
||
/// <summary> | ||
/// The URI string to the JPG file which contains the receipt to be used for tests. | ||
/// </summary> | ||
/// <value>The URI string to the JPG file.</value> | ||
public static string ReceiptUri => string.Format(FileUriFormat, AssetsFolderName, ReceiptFilename); | ||
|
||
/// <summary> | ||
/// Retrieves the relative path to a PDF form available in the test assets. | ||
/// </summary> | ||
/// <param name="index">The index to specify the form to be retrieved.</param> | ||
/// <returns>The relative path to the PDF form corresponding to the specified index.</returns> | ||
public static string RetrieveInvoicePath(int index) | ||
{ | ||
var filename = string.Format(InvoiceFilenameFormat, index); | ||
return Path.Combine(AssetsFolderName, filename); | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the URI string to a PDF form available in the test assets. | ||
/// </summary> | ||
/// <param name="index">The index to specify the form to be retrieved.</param> | ||
/// <returns>The URI string to the PDF form corresponding to the specified index.</returns> | ||
public static string RetrieveInvoiceUri(int index) | ||
{ | ||
var filename = string.Format(InvoiceFilenameFormat, index); | ||
return string.Format(FileUriFormat, AssetsFolderName, filename); | ||
} | ||
} | ||
} |
Oops, something went wrong.