chromium/third_party/tflite_support/src/tensorflow_lite_support/ios/test/task/audio/core/TFLRingBufferTests.m

/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 ==============================================================================*/
#import <XCTest/XCTest.h>

#import "tensorflow_lite_support/ios/sources/TFLCommon.h"
#import "tensorflow_lite_support/ios/task/audio/core/sources/TFLRingBuffer.h"

#define VerifyError(error, expectedErrorDomain, expectedErrorCode, expectedLocalizedDescription) \
  XCTAssertEqualObjects(error.domain, expectedErrorDomain);                                      \
  XCTAssertEqual(error.code, expectedErrorCode);                                                 \
  XCTAssertEqualObjects(error.localizedDescription, expectedLocalizedDescription);

NS_ASSUME_NONNULL_BEGIN

@interface TFLRingBufferTests : XCTestCase
@end

@implementation TFLRingBufferTests

- (void)testLoadSucceedsWithFullLengthBuffer {
  NSInteger inDataLength = 5;
  float inData[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};

  NSInteger bufferSize = 5;
  TFLRingBuffer *ringBuffer = [[TFLRingBuffer alloc] initWithBufferSize:bufferSize];

  XCTAssertTrue([ringBuffer loadFloatData:&(inData[0])
                                 dataSize:inDataLength
                                   offset:0
                                     size:inDataLength
                                    error:nil]);
  // State after load: [1.0, 2.0, 3.0, 4.0, 5.0]

  TFLFloatBuffer *outBuffer = ringBuffer.floatBuffer;
  XCTAssertNotNil(outBuffer);
  XCTAssertEqual(outBuffer.size, bufferSize);

  float expectedData[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};

  for (int i = 0; i < inDataLength; i++) {
    XCTAssertEqual(outBuffer.data[i], expectedData[i]);
  }
}

- (void)testLoadSucceedsWithPartialLengthBuffer {
  NSInteger inDataSize = 3;
  float inData[] = {1.0f, 2.0f, 3.0f};

  NSInteger bufferSize = 5;
  TFLRingBuffer *ringBuffer = [[TFLRingBuffer alloc] initWithBufferSize:bufferSize];

  XCTAssertTrue([ringBuffer loadFloatData:&(inData[0])
                                 dataSize:inDataSize
                                   offset:0
                                     size:inDataSize
                                    error:nil]);

  // State after load: [0.0, 0.0, 1.0, 2.0, 3.0]

  TFLFloatBuffer *outBuffer = ringBuffer.floatBuffer;
  XCTAssertNotNil(outBuffer);
  XCTAssertEqual(outBuffer.size, bufferSize);

  // Expected state after loading most recent elements of source buffer.
  float expectedData[] = {0.0f, 0.0f, 1.0f, 2.0f, 3.0f};

  for (int i = 0; i < bufferSize; i++) {
    XCTAssertEqual(outBuffer.data[i], expectedData[i]);
  }
}

- (void)testLoadSucceedsByShiftingOutOldElements {
  NSInteger initialDataSize = 4;
  float initialArray[] = {1.0f, 2.0f, 3.0f, 4.0f};

  NSInteger bufferSize = 5;
  TFLRingBuffer *ringBuffer = [[TFLRingBuffer alloc] initWithBufferSize:bufferSize];

  XCTAssertTrue([ringBuffer loadFloatData:&(initialArray[0])
                                 dataSize:initialDataSize
                                   offset:0
                                     size:initialDataSize
                                    error:nil]);

  // State after load: [0.0, 1.0, 2.0, 3.0, 4.0]

  NSInteger inDataSize = 3;
  float inArray[] = {5, 6, 7};

  XCTAssertTrue([ringBuffer loadFloatData:&(inArray[0])
                                 dataSize:inDataSize
                                   offset:0
                                     size:inDataSize
                                    error:nil]);

  TFLFloatBuffer *outBuffer = ringBuffer.floatBuffer;
  XCTAssertNotNil(outBuffer);
  XCTAssertEqual(outBuffer.size, bufferSize);

  // Expected state after loading most recent elements of source buffer.
  float expectedData[] = {3.0f, 4.0f, 5.0f, 6.0f, 7.0f};

  for (int i = 0; i < bufferSize; i++) {
    XCTAssertEqual(outBuffer.data[i], expectedData[i]);
  }
}

- (void)testLoadSucceedsWithMostRecentElements {
  NSInteger initialDataSize = 5;
  float initialArray[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};

  NSInteger bufferSize = 5;
  TFLRingBuffer *ringBuffer = [[TFLRingBuffer alloc] initWithBufferSize:bufferSize];

  XCTAssertTrue([ringBuffer loadFloatData:&(initialArray[0])
                                 dataSize:initialDataSize
                                   offset:0
                                     size:initialDataSize
                                    error:nil]);

  // State after load: [1.0, 2.0, 3.0, 4.0, 5.0]

  NSInteger sourceDataSize = 6;
  float sourceArray[] = {6, 7, 8, 9, 10, 11};
  XCTAssertTrue([ringBuffer loadFloatData:&(sourceArray[0])
                                 dataSize:sourceDataSize
                                   offset:0
                                     size:sourceDataSize
                                    error:nil]);

  TFLFloatBuffer *outBuffer = ringBuffer.floatBuffer;
  XCTAssertNotNil(outBuffer);
  XCTAssertEqual(outBuffer.size, bufferSize);

  // Expected state after loading most recent elements of source buffer.
  float expectedData[] = {7.0f, 8.0f, 9.0f, 10.0f, 11.0f};

  for (int i = 0; i < bufferSize; i++) {
    XCTAssertEqual(outBuffer.data[i], expectedData[i]);
  }
}

- (void)testLoadSucceedsWithOffseAndMostRecentElements {
  NSInteger initialDataSize = 5;
  float initialArray[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};

  NSInteger bufferSize = 5;
  TFLRingBuffer *ringBuffer = [[TFLRingBuffer alloc] initWithBufferSize:bufferSize];

  XCTAssertTrue([ringBuffer loadFloatData:&(initialArray[0])
                                 dataSize:initialDataSize
                                   offset:0
                                     size:initialDataSize
                                    error:nil]);

  // State after load: [1.0, 2.0, 3.0, 4.0, 5.0]

  NSInteger totalInSize = 8;
  float inArray[] = {6, 7, 8, 9, 10, 11, 12, 13};

  NSInteger offset = 2;
  NSInteger inDataSize = 6;
  XCTAssertTrue([ringBuffer loadFloatData:&(inArray[0])
                                 dataSize:totalInSize
                                   offset:offset
                                     size:inDataSize
                                    error:nil]);

  TFLFloatBuffer *outBuffer = ringBuffer.floatBuffer;
  XCTAssertNotNil(outBuffer);
  XCTAssertEqual(outBuffer.size, bufferSize);

  // Expected state after load with most recent elements and offset.
  float expectedData[] = {9.0f, 10.0f, 11.0f, 12.0f, 13.0f};

  for (int i = 0; i < bufferSize; i++) {
    XCTAssertEqual(outBuffer.data[i], expectedData[i]);
  }
}

- (void)testLoadSucceedsWithOffset {
  NSInteger initialDataSize = 2;
  float initialArray[] = {1.0f, 2.0f};

  NSInteger bufferSize = 5;
  TFLRingBuffer *ringBuffer = [[TFLRingBuffer alloc] initWithBufferSize:bufferSize];

  XCTAssertTrue([ringBuffer loadFloatData:&(initialArray[0])
                                 dataSize:initialDataSize
                                   offset:0
                                     size:initialDataSize
                                    error:nil]);

  // State after load: [0.0, 0.0, 0.0, 1.0, 2.0]

  NSInteger totalInSize = 4;
  float inArray[] = {6.0f, 7.0f, 8.0f, 9.0f};

  NSInteger offset = 2;
  NSInteger inDataSize = 2;
  XCTAssertTrue([ringBuffer loadFloatData:&(inArray[0])
                                 dataSize:totalInSize
                                   offset:offset
                                     size:inDataSize
                                    error:nil]);

  TFLFloatBuffer *outBuffer = ringBuffer.floatBuffer;
  XCTAssertNotNil(outBuffer);
  XCTAssertEqual(outBuffer.size, bufferSize);

  // State after load with offset
  float expectedData[] = {0.0f, 1.0f, 2.0f, 8.0f, 9.0f};

  for (int i = 0; i < bufferSize; i++) {
    XCTAssertEqual(outBuffer.data[i], expectedData[i]);
  }
}

- (void)testLoadFailsWithIndexOutofBounds {
  NSInteger initialDataSize = 2;
  float initialArray[] = {1.0f, 2.0f};

  NSInteger bufferSize = 5;
  TFLRingBuffer *ringBuffer = [[TFLRingBuffer alloc] initWithBufferSize:bufferSize];

  XCTAssertTrue([ringBuffer loadFloatData:&(initialArray[0])
                                 dataSize:initialDataSize
                                   offset:0
                                     size:initialDataSize
                                    error:nil]);

  float inArray[] = {6.0f, 7.0f, 8.0f, 9.0f};

  NSInteger offset = 2;
  NSInteger inDataSize = 3;

  NSError *error = nil;
  XCTAssertFalse([ringBuffer loadFloatData:&(inArray[0])
                                  dataSize:initialDataSize
                                    offset:offset
                                      size:inDataSize
                                     error:&error]);

  XCTAssertNotNil(error);
  VerifyError(error, @"org.tensorflow.lite.tasks", TFLSupportErrorCodeInvalidArgumentError,
              @"offset + size exceeds the maximum size of the source buffer.");
}

- (void)testClearSucceeds {
  NSInteger initialDataSize = 2;
  float initialArray[] = {1.0f, 2.0f};

  NSInteger bufferSize = 5;
  TFLRingBuffer *ringBuffer = [[TFLRingBuffer alloc] initWithBufferSize:bufferSize];

  XCTAssertTrue([ringBuffer loadFloatData:&(initialArray[0])
                                 dataSize:initialDataSize
                                   offset:0
                                     size:initialDataSize
                                    error:nil]);

  [ringBuffer clear];

  float expectedData[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f};

  TFLFloatBuffer *outBuffer = ringBuffer.floatBuffer;
  XCTAssertNotNil(outBuffer);
  XCTAssertEqual(outBuffer.size, bufferSize);

  for (int i = 0; i < bufferSize; i++) {
    XCTAssertEqual(outBuffer.data[i], expectedData[i]);
  }
}

@end

NS_ASSUME_NONNULL_END