xref: /aosp_15_r20/external/tensorflow/tensorflow/lite/objc/tests/TFLInterpreterTests.m (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at:
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#import "tensorflow/lite/objc/apis/TFLTensorFlowLite.h"
16
17#import <XCTest/XCTest.h>
18
19NS_ASSUME_NONNULL_BEGIN
20
21/**
22 * Regular expression for TensorFlow Lite runtime version string, e.g. "1.14.0", "0.1.2-alpha.1",
23 * "0.3.4-beta2", "1.14.0-rc.3".
24 */
25static NSString *const kTFLVersionRegex = @"^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?$";
26
27/** Float model resource name. */
28static NSString *const kAddFloatModelResourceName = @"add";
29
30/** Quantized model resource name. */
31static NSString *const kAddQuantizedModelResourceName = @"add_quantized";
32
33/** Model resource type. */
34static NSString *const kAddModelResourceType = @"bin";
35
36/** Size of the first (and only) dimension of the input and output tensor in the Add model. */
37enum EnumType : NSUInteger {kAddModelTensorFirstDimensionSize = 2U};
38
39/** Quantization scale of the quantized model. */
40static const float kAddQuantizedModelScale = 0.003922F;
41
42/** Quantization zero point of the quantized model. */
43static const int32_t kAddQuantizedModelZeroPoint = 0;
44
45/** Invalid input tensor index. */
46static const NSUInteger kInvalidInputTensorIndex = 1U;
47
48/** Invalid output tensor index. */
49static const NSUInteger kInvalidOutputTensorIndex = 1U;
50
51/** Accuracy used in comparing floating numbers. */
52static const float kTestAccuracy = 1E-5F;
53/**
54 * Unit tests for TFLInterpreter.
55 */
56@interface TFLInterpreterTests : XCTestCase
57
58/** Absolute path of the Add float model resource. */
59@property(nonatomic, nullable) NSString *floatModelPath;
60
61/** Default interpreter using the Add model. */
62@property(nonatomic, nullable) TFLInterpreter *interpreter;
63
64@end
65
66@implementation TFLInterpreterTests
67
68#pragma mark - XCTestCase
69
70- (void)setUp {
71  [super setUp];
72
73  NSBundle *bundle = [NSBundle bundleForClass:[self class]];
74  self.floatModelPath = [bundle pathForResource:kAddFloatModelResourceName
75                                         ofType:kAddModelResourceType];
76  NSError *error;
77  self.interpreter = [[TFLInterpreter alloc] initWithModelPath:self.floatModelPath error:&error];
78  XCTAssertNil(error);
79  XCTAssertNotNil(self.interpreter);
80  XCTAssertTrue([self.interpreter allocateTensorsWithError:nil]);
81}
82
83- (void)tearDown {
84  self.floatModelPath = nil;
85  self.interpreter = nil;
86
87  [super tearDown];
88}
89
90#pragma mark - Tests
91
92- (void)testTFLVersion {
93  NSRange range = [TFLVersion rangeOfString:kTFLVersionRegex options:NSRegularExpressionSearch];
94  XCTAssertNotEqual(range.location, NSNotFound);
95}
96
97- (void)testSuccessfulFullRunAddFloatModel {
98  // Shape for both input and output tensor.
99  NSArray<NSNumber *> *shape = @[ @(kAddModelTensorFirstDimensionSize) ];
100
101  // Creates the interpreter options.
102  TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init];
103  XCTAssertNotNil(options);
104  options.numberOfThreads = 2;
105
106  // Creates the interpreter.
107  NSError *error;
108  TFLInterpreter *customInterpreter = [[TFLInterpreter alloc] initWithModelPath:self.floatModelPath
109                                                                        options:options
110                                                                      delegates:@[]
111                                                                          error:&error];
112  XCTAssertNil(error);
113  XCTAssertNotNil(customInterpreter);
114
115  // Allocates memory for tensors.
116  XCTAssertTrue([customInterpreter allocateTensorsWithError:&error]);
117  XCTAssertNil(error);
118
119  // Verifies input and output tensor counts.
120  XCTAssertEqual(customInterpreter.inputTensorCount, 1);
121  XCTAssertEqual(customInterpreter.outputTensorCount, 1);
122
123  // Resizes the intput tensor.
124  XCTAssertTrue([customInterpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]);
125  XCTAssertNil(error);
126
127  // Re-allocates memory for tensors.
128  XCTAssertTrue([customInterpreter allocateTensorsWithError:&error]);
129  XCTAssertNil(error);
130
131  // Verifies the input tensor.
132  TFLTensor *inputTensor = [customInterpreter inputTensorAtIndex:0 error:&error];
133  XCTAssertNotNil(inputTensor);
134  XCTAssertNil(error);
135  XCTAssertTrue([inputTensor.name isEqualToString:@"input"]);
136  XCTAssertEqual(inputTensor.dataType, TFLTensorDataTypeFloat32);
137  NSArray<NSNumber *> *inputTensorShape = [inputTensor shapeWithError:&error];
138  XCTAssertNil(error);
139  XCTAssertTrue([shape isEqualToArray:inputTensorShape]);
140
141  // Copies the input data.
142  NSMutableData *inputData = [NSMutableData dataWithCapacity:0];
143  float one = 1.f;
144  float three = 3.f;
145  [inputData appendBytes:&one length:sizeof(float)];
146  [inputData appendBytes:&three length:sizeof(float)];
147  XCTAssertTrue([inputTensor copyData:inputData error:&error]);
148  XCTAssertNil(error);
149
150  // Invokes the interpreter.
151  XCTAssertTrue([customInterpreter invokeWithError:&error]);
152  XCTAssertNil(error);
153
154  // Verifies the output tensor.
155  TFLTensor *outputTensor = [customInterpreter outputTensorAtIndex:0 error:&error];
156  XCTAssertNotNil(outputTensor);
157  XCTAssertNil(error);
158  XCTAssertTrue([outputTensor.name isEqualToString:@"output"]);
159  XCTAssertEqual(outputTensor.dataType, TFLTensorDataTypeFloat32);
160  NSArray<NSNumber *> *outputTensorShape = [outputTensor shapeWithError:&error];
161  XCTAssertNil(error);
162  XCTAssertTrue([shape isEqualToArray:outputTensorShape]);
163
164  // Tries to query an invalid output tensor index.
165  TFLTensor *invalidOutputTensor = [customInterpreter outputTensorAtIndex:kInvalidOutputTensorIndex
166                                                                    error:&error];
167  XCTAssertNil(invalidOutputTensor);
168  XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidTensorIndex);
169
170  // Gets the output tensor data.
171  error = nil;
172  NSData *outputData = [outputTensor dataWithError:&error];
173  XCTAssertNotNil(outputData);
174  XCTAssertNil(error);
175  float output[kAddModelTensorFirstDimensionSize];
176  [outputData getBytes:output length:(sizeof(float) * kAddModelTensorFirstDimensionSize)];
177  XCTAssertEqualWithAccuracy(output[0], 3.f, kTestAccuracy);
178  XCTAssertEqualWithAccuracy(output[1], 9.f, kTestAccuracy);
179}
180
181- (void)testSuccessfulFullRunQuantizedModel {
182  // Shape for both input and output tensor.
183  NSArray<NSNumber *> *shape = @[ @(kAddModelTensorFirstDimensionSize) ];
184
185  // Creates the interpreter options.
186  TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init];
187  XCTAssertNotNil(options);
188  options.numberOfThreads = 2;
189
190  NSBundle *bundle = [NSBundle bundleForClass:[self class]];
191  NSString *quantizedModelPath = [bundle pathForResource:kAddQuantizedModelResourceName
192                                                  ofType:kAddModelResourceType];
193
194  // Creates the interpreter.
195  NSError *error;
196  TFLInterpreter *customInterpreter = [[TFLInterpreter alloc] initWithModelPath:quantizedModelPath
197                                                                        options:options
198                                                                      delegates:@[]
199                                                                          error:&error];
200  XCTAssertNil(error);
201  XCTAssertNotNil(customInterpreter);
202
203  // Allocates memory for tensors.
204  XCTAssertTrue([customInterpreter allocateTensorsWithError:&error]);
205  XCTAssertNil(error);
206
207  // Verifies input and output tensor counts.
208  XCTAssertEqual(customInterpreter.inputTensorCount, 1);
209  XCTAssertEqual(customInterpreter.outputTensorCount, 1);
210
211  // Resizes the intput tensor.
212  XCTAssertTrue([customInterpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]);
213  XCTAssertNil(error);
214
215  // Re-allocates memory for tensors.
216  XCTAssertTrue([customInterpreter allocateTensorsWithError:&error]);
217  XCTAssertNil(error);
218
219  // Verifies the input tensor.
220  TFLTensor *inputTensor = [customInterpreter inputTensorAtIndex:0 error:&error];
221  XCTAssertNotNil(inputTensor);
222  XCTAssertNil(error);
223  XCTAssertTrue([inputTensor.name isEqualToString:@"input"]);
224  XCTAssertEqual(inputTensor.dataType, TFLTensorDataTypeUInt8);
225  XCTAssertEqualWithAccuracy(inputTensor.quantizationParameters.scale, kAddQuantizedModelScale,
226                             kTestAccuracy);
227  XCTAssertEqual(inputTensor.quantizationParameters.zeroPoint, kAddQuantizedModelZeroPoint);
228  NSArray<NSNumber *> *inputTensorShape = [inputTensor shapeWithError:&error];
229  XCTAssertNil(error);
230  XCTAssertTrue([shape isEqualToArray:inputTensorShape]);
231
232  // Copies the input data.
233  NSMutableData *inputData = [NSMutableData dataWithCapacity:0];
234  uint8_t one = 1;
235  uint8_t three = 3;
236  [inputData appendBytes:&one length:sizeof(uint8_t)];
237  [inputData appendBytes:&three length:sizeof(uint8_t)];
238  XCTAssertTrue([inputTensor copyData:inputData error:&error]);
239  XCTAssertNil(error);
240
241  // Invokes the interpreter.
242  XCTAssertTrue([customInterpreter invokeWithError:&error]);
243  XCTAssertNil(error);
244
245  // Verifies the output tensor.
246  TFLTensor *outputTensor = [customInterpreter outputTensorAtIndex:0 error:&error];
247  XCTAssertNotNil(outputTensor);
248  XCTAssertNil(error);
249  XCTAssertTrue([outputTensor.name isEqualToString:@"output"]);
250  XCTAssertEqual(outputTensor.dataType, TFLTensorDataTypeUInt8);
251  XCTAssertEqualWithAccuracy(outputTensor.quantizationParameters.scale, kAddQuantizedModelScale,
252                             kTestAccuracy);
253  XCTAssertEqual(outputTensor.quantizationParameters.zeroPoint, kAddQuantizedModelZeroPoint);
254  NSArray<NSNumber *> *outputTensorShape = [outputTensor shapeWithError:&error];
255  XCTAssertNil(error);
256  XCTAssertTrue([shape isEqualToArray:outputTensorShape]);
257
258  // Tries to query an invalid output tensor index.
259  TFLTensor *invalidOutputTensor = [customInterpreter outputTensorAtIndex:kInvalidOutputTensorIndex
260                                                                    error:&error];
261  XCTAssertNil(invalidOutputTensor);
262  XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidTensorIndex);
263
264  // Gets the output tensor data.
265  error = nil;
266  NSData *outputData = [outputTensor dataWithError:&error];
267  XCTAssertNotNil(outputData);
268  XCTAssertNil(error);
269  uint8_t output[kAddModelTensorFirstDimensionSize];
270  [outputData getBytes:output length:(sizeof(uint8_t) * kAddModelTensorFirstDimensionSize)];
271  XCTAssertEqual(output[0], 3);
272  XCTAssertEqual(output[1], 9);
273}
274
275- (void)testInitWithModelPath_invalidPath {
276  // Creates the interpreter.
277  NSError *error;
278  TFLInterpreter *brokenInterpreter = [[TFLInterpreter alloc] initWithModelPath:@"InvalidPath"
279                                                                          error:&error];
280  XCTAssertNil(brokenInterpreter);
281  XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToLoadModel);
282}
283
284- (void)testInvoke_beforeAllocation {
285  NSError *error;
286  TFLInterpreter *interpreterWithoutAllocation =
287      [[TFLInterpreter alloc] initWithModelPath:self.floatModelPath error:&error];
288  XCTAssertNotNil(interpreterWithoutAllocation);
289  XCTAssertNil(error);
290
291  XCTAssertFalse([interpreterWithoutAllocation invokeWithError:&error]);
292  XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToInvoke);
293}
294
295- (void)testInputTensorAtIndex_invalidIndex {
296  NSError *error;
297  TFLTensor *inputTensor = [self.interpreter inputTensorAtIndex:kInvalidInputTensorIndex
298                                                          error:&error];
299  XCTAssertNil(inputTensor);
300  XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidTensorIndex);
301}
302
303- (void)testResizeInputTensorAtIndex_invalidIndex {
304  NSArray<NSNumber *> *shape = @[ @(kAddModelTensorFirstDimensionSize) ];
305  NSError *error;
306  XCTAssertFalse([self.interpreter resizeInputTensorAtIndex:kInvalidInputTensorIndex
307                                                    toShape:shape
308                                                      error:&error]);
309  XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidTensorIndex);
310}
311
312- (void)testResizeInputTensorAtIndex_emptyShape {
313  NSMutableArray<NSNumber *> *emptyShape = [NSMutableArray arrayWithCapacity:0];
314  NSError *error;
315  XCTAssertFalse([self.interpreter resizeInputTensorAtIndex:0 toShape:emptyShape error:&error]);
316  XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidShape);
317}
318
319- (void)testResizeInputTensorAtIndex_zeroDimensionSize {
320  NSArray<NSNumber *> *shape = @[ @0 ];
321  NSError *error;
322  XCTAssertFalse([self.interpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]);
323  XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidShape);
324}
325
326- (void)testCopyDataToInputTensorAtIndex_invalidInputDataByteSize {
327  NSMutableData *inputData = [NSMutableData dataWithCapacity:0];
328  float one = 1.f;
329  float three = 3.f;
330  [inputData appendBytes:&one length:sizeof(float)];
331  [inputData appendBytes:&three length:(sizeof(float) - 1)];
332  NSError *error;
333  TFLTensor *inputTensor = [self.interpreter inputTensorAtIndex:0 error:&error];
334  XCTAssertNotNil(inputTensor);
335  XCTAssertNil(error);
336  XCTAssertFalse([inputTensor copyData:inputData error:&error]);
337  XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidInputByteSize);
338}
339
340- (void)testCopyDataToOutputTensorAtIndex_notAllowed {
341  NSMutableData *data = [NSMutableData dataWithCapacity:0];
342  float one = 1.f;
343  float three = 3.f;
344  [data appendBytes:&one length:sizeof(float)];
345  [data appendBytes:&three length:(sizeof(float) - 1)];
346  NSError *error;
347  TFLTensor *outputTensor = [self.interpreter outputTensorAtIndex:0 error:&error];
348  XCTAssertNotNil(outputTensor);
349  XCTAssertNil(error);
350  XCTAssertFalse([outputTensor copyData:data error:&error]);
351  XCTAssertEqual(error.code, TFLInterpreterErrorCodeCopyDataToOutputTensorNotAllowed);
352}
353
354- (void)testNilCDelegate {
355  // Creates the interpreter options.
356  TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init];
357
358  // Creates the interpreter.
359  NSError *error;
360  TFLDelegate *delegate = [[TFLDelegate alloc] init];  // Base delegate's cDelegate is nil.
361  TFLInterpreter *customInterpreter = [[TFLInterpreter alloc] initWithModelPath:self.floatModelPath
362                                                                        options:options
363                                                                      delegates:@[ delegate ]
364                                                                          error:&error];
365  XCTAssertNil(error);
366  XCTAssertNotNil(customInterpreter);
367
368  // Allocates memory for tensors.
369  XCTAssertTrue([customInterpreter allocateTensorsWithError:&error]);
370  XCTAssertNil(error);
371}
372
373@end
374
375NS_ASSUME_NONNULL_END
376