spiegel-keyman/developer/src/kmcmplib/tests/kmcompx.tests.cpp
Eberhard Beilharz 66404f9503
chore(developer): rename test files
This change rename the test files for Developer according to the
discussion at the Keyman conference in November 2024.
2024-11-28 14:32:09 +01:00

212 lines
6.4 KiB
C++

/*
* Keyman is copyright (C) SIL International. MIT License.
*
* Tiny frame console app to compile a .kmx from a .kmn
*/
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <locale>
#include <vector>
#include <string>
#include <sstream>
#include <kmcmplibapi.h>
#include <kmx_file.h>
#include "util_filesystem.h"
#include "util_callbacks.h"
#ifdef _MSC_VER
#else
#include <unistd.h>
#include <cstring>
#endif
using namespace std;
int main(int argc, char *argv[])
{
if(argc < 4) {
puts("Usage: kmcompxtest source.kmn output.kmx fixture.kmx");
return __LINE__;
}
char* kmn_file = argv[1];
char* kmx_file = argv[2];
char* reference_kmx = argv[3];
// Replace \ with /
for(char *p = kmn_file; *p; p++) {
if(*p == '\\') *p = '/';
}
for(char *p = kmx_file; *p; p++) {
if(*p == '\\') *p = '/';
}
for(char *p = reference_kmx; *p; p++) {
if(*p == '\\') *p = '/';
}
const char* first5 = "CERR_";
KMCMP_COMPILER_RESULT result;
KMCMP_COMPILER_OPTIONS options;
options.saveDebug = true;
options.compilerWarningsAsErrors = false;
options.warnDeprecatedCode = true;
options.shouldAddCompilerVersion = false;
options.target = CKF_KEYMAN;
if(kmcmp_CompileKeyboard(kmn_file, options, msgproc, loadfileProc, nullptr, result)) {
char* testname = strrchr( (char*) kmn_file, '/') + 1;
if(strncmp(testname, first5, strlen(first5)) == 0){
return __LINE__; // exit code: CERR_ in Name + no Error found
}
// On non-win32 platforms, we cannot get kmcmpdll.dll to build keyboards
// legacy-mode, so we'll compare to a hopefully existing file that we've
// been passed
FILE* fp1 = Open_File(kmx_file, "wb");
if(!fp1) return __LINE__;
// Write out for reference
fwrite(result.kmx, 1, result.kmxSize, fp1);
fclose(fp1);
FILE* fp2 = Open_File(reference_kmx, "rb");
if(!fp2) return __LINE__; // exit code: fail if no reference kmx file in build-folder
fseek(fp2, 0, SEEK_END);
auto sz2 = ftell(fp2);
fseek(fp2, 0, SEEK_SET);
if (result.kmxSize != (size_t)sz2) return __LINE__; // exit code: size of kmx-file in build differs from size of kmx-file in source folder
char* buf2 = new char[result.kmxSize];
auto sz3 = fread(buf2, 1, result.kmxSize, fp2);
if (result.kmxSize != sz3) return __LINE__; // exit code: when not able to read the build into the buffer
return memcmp(result.kmx, buf2, result.kmxSize) ? __LINE__ : 0; // exit code: when contents of kmx-file in build differs from contents of kmx-file in source folder
// success: when contents of kmx-file in build and source folder are the same
}
else { /*if Errors found: check number (e.g. CERR_4061_balochi_phonetic.kmn should produce Error 4061)*/
int error_val = 0;
char* testname = strrchr( (char*) kmn_file, '/') + 1;
// Does testname contain CERR_ && contains '_' on pos 9 ? -> Get ErrorValue
if ((strncmp(testname, first5, strlen(first5)) == 0) && (testname[9] == '_')) {
char* ErrNr = strchr(testname, '_') +1 ;
std::istringstream(ErrNr) >> std::hex >> error_val;
// check if error_val is in Array of Errors; if it is found return 0 (it's not an error)
for (size_t i = 0; i < error_vec.size() ; i++) {
if (error_vec[i] == error_val) {
return 0; // success: CERR_ in Name + Error (specified in CERR_Name) IS found
}
}
return __LINE__; // exit code: CERR_ in Name + Error (specified in CERR_Name) is NOT found
}
return __LINE__; // exit code: no correct CERR_ in Name + CompileKeyboardFile failed (i.e. test config is invalid)
}
}
bool equalIgnoreCase(const string& a, const string& b)
{
return std::equal(
a.begin(), a.end(),
b.begin(), b.end(),
[](char a, char b) {
return tolower(a) == tolower(b);
}
);
}
// Returns TRUE if the keyboard has a &TARGETS store with
// 'any', 'windows', 'macosx', 'linux', or 'desktop' targets,
// or if no &TARGETS store exists (which is equivalent to
// 'windows').
bool isDesktopKeyboard(FILE* fp) {
COMP_KEYBOARD fk;
if(fseek(fp, 0, SEEK_SET) != 0) {
printf("isDesktopKeyboard: fseek(0) failed\n");
exit(1);
}
if(!fread(&fk, sizeof(fk), 1, fp)) {
printf("isDesktopKeyboard failed: could not read COMP_KEYBOARD\n");
exit(1);
}
// Note: this code assumes a valid .kmx. This is okay for a unit test
if(fseek(fp, fk.dpStoreArray, SEEK_SET) != 0) {
printf("isDesktopKeyboard failed: could not locate fk.dpStoreArray=%x\n", fk.dpStoreArray);
exit(1);
}
PCOMP_STORE pfs = new COMP_STORE[fk.cxStoreArray];
if(fread(pfs, sizeof(COMP_STORE), fk.cxStoreArray, fp) != fk.cxStoreArray) {
printf("isDesktopKeyboard failed: could not read fk.dpStoreArray=%x [%x]\n", fk.dpStoreArray, fk.cxStoreArray);
exit(1);
}
PCOMP_STORE s = pfs;
for(KMX_DWORD i = 0; i < fk.cxStoreArray; i++, s++) {
if(s->dwSystemID != TSS_TARGETS) {
continue;
}
// Read targets store
if(fseek(fp, s->dpString, SEEK_SET) != 0) {
printf("isDesktopKeyboard: could not seek to TSS_TARGETS.dpString %x\n", s->dpString);
exit(1);
}
KMX_WCHAR buf[256];
if(fread(buf, sizeof(KMX_WCHAR), 256, fp) == 0) {
printf("isDesktopKeyboard: could not read TSS_TARGETS.dpString %x\n", s->dpString);
exit(1);
}
buf[255] = 0; // sanity
if(fseek(fp, 0, SEEK_SET) != 0) {
printf("isDesktopKeyboard: fseek(0) failed\n");
exit(1);
}
std::string targets1 = string_from_u16string(buf);
std::stringstream targets(targets1);
std::string target;
printf("isDesktopKeyboard: TSS_TARGETS: %s\n", targets1.c_str());
delete[] pfs;
while(targets >> target) {
//Matches KMXKeymanTargets from UKeymanTargets.pas
if(equalIgnoreCase(target, "any") ||
equalIgnoreCase(target, "windows") ||
equalIgnoreCase(target, "macosx") ||
equalIgnoreCase(target, "linux") ||
equalIgnoreCase(target, "desktop")
) {
printf("isDesktopKeyboard: TSS_TARGETS does contain desktop target %s\n", target.c_str());
return true;
}
}
printf("isDesktopKeyboard: TSS_TARGETS does not contain desktop target\n");
return false;
}
delete[] pfs;
if(fseek(fp, 0, SEEK_SET) != 0) {
printf("isDesktopKeyboard: fseek(0) failed\n");
exit(1);
}
printf("isDesktopKeyboard: TSS_TARGETS does not exist\n");
return true;
}