// // 8088Tests.m // Clock SignalTests // // Created by Thomas Harte on 13/09/2023. // Copyright © 2023 Thomas Harte. All rights reserved. // #import #include #include #include #include #include "NSData+dataWithContentsOfGZippedFile.h" #include "../../../InstructionSets/x86/Decoder.hpp" namespace { // The tests themselves are not duplicated in this repository; // provide their real path here. constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"; } @interface i8088Tests : XCTestCase @end @implementation i8088Tests - (NSArray *)testFiles { NSString *path = [NSString stringWithUTF8String:TestSuiteHome]; NSSet *allowList = nil; // [[NSSet alloc] initWithArray:@[ // @"00.json.gz", // ]]; NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil]; files = [files filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString* evaluatedObject, NSDictionary *) { if(allowList && ![allowList containsObject:[evaluatedObject lastPathComponent]]) { return NO; } return [evaluatedObject hasSuffix:@"json.gz"]; }]]; NSMutableArray *fullPaths = [[NSMutableArray alloc] init]; for(NSString *file in files) { [fullPaths addObject:[path stringByAppendingPathComponent:file]]; } return fullPaths; } - (bool)applyDecodingTest:(NSDictionary *)test { using Decoder = InstructionSet::x86::Decoder; Decoder decoder; // Build a vector of the instruction; this makes manual step debugging easier. NSArray *encoding = test[@"bytes"]; std::vector data; data.reserve(encoding.count); for(NSNumber *number in encoding) { data.push_back([number intValue]); } const auto decoded = decoder.decode(data.data(), data.size()); XCTAssert( decoded.first == [encoding count], "Wrong length of instruction decoded for %@ — decoded %d rather than %lu", test[@"name"], decoded.first, (unsigned long)[encoding count] ); if(decoded.first != [encoding count]) { NSMutableString *hexInstruction = [[NSMutableString alloc] init]; for(uint8_t byte: data) { [hexInstruction appendFormat:@"%02x ", byte]; } NSLog(@"Instruction was %@", hexInstruction); // Repeat the decoding, for ease of debugging. Decoder straw_man; straw_man.decode(data.data(), data.size()); return false; } // Form string version, compare. std::string operation; using Operation = InstructionSet::x86::Operation; int operands = 0; switch(decoded.second.operation) { case Operation::SUB: operation += "sub"; operands = 2; break; default: break; } auto to_string = [] (InstructionSet::x86::DataPointer pointer) { std::string operand; using Source = InstructionSet::x86::Source; switch(pointer.source()) { } switch(pointer.index()) { default: break; case Source::eAX: operand += "ax"; break; } return operand; }; if(operands > 1) { operation += " "; operation += to_string(decoded.second.destination()); operation += ","; } if(operands > 0) { operation += " "; operation += to_string(decoded.second.source()); } XCTAssertEqual([NSString stringWithUTF8String:operation.c_str()], test[@"name"]); return true; } - (void)testDecoding { NSMutableSet *failures = [[NSMutableSet alloc] init]; NSArray *testFiles = [self testFiles]; for(NSString *file in testFiles) { NSData *data = [NSData dataWithContentsOfGZippedFile:file]; NSArray *testsInFile = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSUInteger successes = 0; for(NSDictionary *test in testsInFile) { // A single failure per instruction is fine. if(![self applyDecodingTest:test]) { [failures addObject:file]; break; } ++successes; } if(successes != [testsInFile count]) { NSLog(@"Failed after %ld successes", successes); } } NSLog(@"%ld failures out of %ld tests: %@", failures.count, testFiles.count, [[failures allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]); } @end