Java: added compiled metadata generation

This commit is contained in:
Vlasislav Kashin
2025-07-11 03:46:50 +03:00
parent 8a42c0ecb5
commit 10c5ee1159
18 changed files with 222 additions and 247 deletions

View File

@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 The Libphonenumber Authors
* Copyright (C) 2025 The Kashin Vladislav (modified)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,9 +22,9 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.i18n.phonenumbers.BuildMetadataCppFromXml.Options;
import com.google.i18n.phonenumbers.BuildMetadataCppFromXml.Variant;
import com.google.i18n.phonenumbers.CppMetadataGenerator.Type;
import com.google.i18n.phonenumbers.BuildMetadataRustFromXml.Options;
import com.google.i18n.phonenumbers.BuildMetadataRustFromXml.Variant;
import com.google.i18n.phonenumbers.RustMetadataGenerator.Type;
import org.junit.Test;
@@ -36,7 +37,7 @@ import java.nio.charset.Charset;
* Tests the BuildMetadataCppFromXml implementation to make sure it parses command line options and
* generates code correctly.
*/
public class BuildMetadataCppFromXmlTest {
public class BuildMetadataRustFromXmlTest {
// Various repeated test strings and data.
private static final String IGNORED = "IGNORED";
@@ -44,7 +45,9 @@ public class BuildMetadataCppFromXmlTest {
private static final String INPUT_PATH_XML = "input/path.xml";
private static final byte[] TEST_DATA =
new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
private static final String CPP_TEST_DATA = "0xCA, 0xFE, 0xBA, 0xBE";
private static final int TEST_DATA_LEN = TEST_DATA.length;
private static final String TEST_CONSTANT_NAME = "METADATA";
private static final String OUTPUT_DATA = "0xCA, 0xFE, 0xBA, 0xBE";
@Test
public void parseVariant() {
@@ -60,7 +63,7 @@ public class BuildMetadataCppFromXmlTest {
@Test
public void parseBadOptions() {
try {
BuildMetadataCppFromXml.Options.parse("MyCommand", new String[] { IGNORED });
BuildMetadataRustFromXml.Options.parse("MyCommand", new String[] { IGNORED });
fail("Expected exception not thrown");
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("MyCommand"));
@@ -69,93 +72,58 @@ public class BuildMetadataCppFromXmlTest {
@Test
public void parseGoodOptions() {
Options opt = BuildMetadataCppFromXml.Options.parse("MyCommand",
new String[] { IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "test_alternate_format" });
Options opt = BuildMetadataRustFromXml.Options.parse("MyCommand",
new String[] { IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "test_alternate_format", "--const-name=" + TEST_CONSTANT_NAME });
assertEquals(Type.ALTERNATE_FORMAT, opt.getType());
assertEquals(Variant.TEST, opt.getVariant());
assertEquals(INPUT_PATH_XML, opt.getInputFilePath());
assertEquals(OUTPUT_DIR, opt.getOutputDir());
assertEquals(TEST_CONSTANT_NAME, opt.getConstantName());
}
@Test
public void generateMetadata() {
String[] args = new String[] {
IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "metadata" };
IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "metadata", "--const-name " + TEST_CONSTANT_NAME };
// Most of the useful asserts are done in the mock class.
MockedCommand command = new MockedCommand(
INPUT_PATH_XML, false, OUTPUT_DIR, Type.METADATA, Variant.FULL);
INPUT_PATH_XML, false, OUTPUT_DIR, Type.METADATA, Variant.FULL, TEST_CONSTANT_NAME
);
command.setArgs(args);
command.start();
// Sanity check the captured data (asserting implicitly that the mocked methods were called).
String headerString = command.capturedHeaderFile();
assertTrue(headerString.contains("const void* metadata_get()"));
assertTrue(headerString.contains("int metadata_size()"));
String sourceString = command.capturedSourceFile();
assertTrue(sourceString.contains("const void* metadata_get()"));
assertTrue(sourceString.contains("int metadata_size()"));
assertTrue(sourceString.contains(CPP_TEST_DATA));
assertTrue(sourceString.contains("pub const "+TEST_CONSTANT_NAME+": [u8; " + TEST_DATA_LEN + "] ="));
assertTrue(sourceString.contains(OUTPUT_DATA));
assertTrue(sourceString.contains("];"));
}
@Test
public void generateLiteMetadata() {
String[] args = new String[] {
IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "lite_metadata" };
// Most of the useful asserts are done in the mock class.
MockedCommand command = new MockedCommand(
INPUT_PATH_XML, true, OUTPUT_DIR, Type.METADATA, Variant.LITE);
command.setArgs(args);
command.start();
// Sanity check the captured data (asserting implicitly that the mocked methods were called).
String headerString = command.capturedHeaderFile();
assertTrue(headerString.contains("const void* metadata_get()"));
assertTrue(headerString.contains("int metadata_size()"));
String sourceString = command.capturedSourceFile();
assertTrue(sourceString.contains("const void* metadata_get()"));
assertTrue(sourceString.contains("int metadata_size()"));
assertTrue(sourceString.contains(CPP_TEST_DATA));
}
@Test
public void generateAlternateFormat() {
String[] args = new String[] {
IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "alternate_format" };
// Most of the useful asserts are done in the mock class.
MockedCommand command = new MockedCommand(
INPUT_PATH_XML, false, OUTPUT_DIR, Type.ALTERNATE_FORMAT, Variant.FULL);
command.setArgs(args);
command.start();
// Sanity check the captured data (asserting implicitly that the mocked methods were called).
String headerString = command.capturedHeaderFile();
assertTrue(headerString.contains("const void* alternate_format_get()"));
assertTrue(headerString.contains("int alternate_format_size()"));
String sourceString = command.capturedSourceFile();
assertTrue(sourceString.contains("const void* alternate_format_get()"));
assertTrue(sourceString.contains("int alternate_format_size()"));
assertTrue(sourceString.contains(CPP_TEST_DATA));
}
// no need test for metadata with other names since it's set with parameter
/**
* Manually mocked subclass of BuildMetadataCppFromXml which overrides all file related behavior
* while asserting the validity of any parameters passed to the mocked methods. After starting
* this command, the captured header and source file contents can be retrieved for testing.
*/
static class MockedCommand extends BuildMetadataCppFromXml {
static class MockedCommand extends BuildMetadataRustFromXml {
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final String expectedInputFilePath;
private final boolean expectedLiteMetadata;
private final String expectedOutputDirPath;
private final Type expectedType;
private final Variant expectedVariant;
private final ByteArrayOutputStream headerOut = new ByteArrayOutputStream();
private final String expectedConstantName;
private final ByteArrayOutputStream sourceOut = new ByteArrayOutputStream();
public MockedCommand(String expectedInputFilePath, boolean expectedLiteMetadata,
String expectedOutputDirPath, Type expectedType, Variant expectedVariant) {
String expectedOutputDirPath, Type expectedType, Variant expectedVariant,
String expectedConstantName) {
this.expectedInputFilePath = expectedInputFilePath;
this.expectedLiteMetadata = expectedLiteMetadata;
this.expectedOutputDirPath = expectedOutputDirPath;
this.expectedType = expectedType;
this.expectedConstantName = expectedConstantName;
this.expectedVariant = expectedVariant;
}
@Override void writePhoneMetadataCollection(
@@ -164,20 +132,11 @@ public class BuildMetadataCppFromXmlTest {
assertEquals(expectedLiteMetadata, liteMetadata);
out.write(TEST_DATA, 0, TEST_DATA.length);
}
@Override OutputStream openHeaderStream(File dir, Type type) {
@Override OutputStream openSourceStream(File dir) {
assertEquals(expectedOutputDirPath, dir.getPath());
assertEquals(expectedType, type);
return headerOut;
}
@Override OutputStream openSourceStream(File dir, Type type, Variant variant) {
assertEquals(expectedOutputDirPath, dir.getPath());
assertEquals(expectedType, type);
assertEquals(expectedVariant, variant);
return sourceOut;
}
String capturedHeaderFile() {
return new String(headerOut.toByteArray(), UTF_8);
}
String capturedSourceFile() {
return new String(sourceOut.toByteArray(), UTF_8);
}

View File

@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012 The Libphonenumber Authors
*
* Copyright (C) 2025 The Kashin Vladislav (modified)
*
* 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
@@ -19,7 +20,7 @@ package com.google.i18n.phonenumbers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.google.i18n.phonenumbers.CppMetadataGenerator.Type;
import com.google.i18n.phonenumbers.RustMetadataGenerator.Type;
import org.junit.Test;
@@ -35,60 +36,42 @@ import java.util.List;
/**
* Tests that the CppXmlMetadata class emits the expected source and header files for metadata.
*/
public class CppMetadataGeneratorTest {
public class RustMetadataGeneratorTest {
@Test
public void emitStaticArrayData() {
// 13 bytes per line, so have 16 bytes to test > 1 line (general case).
// Use all hex digits in both nibbles to test hex formatting.
byte[] data = new byte[] {
// 13 bytes per line, so have 16 bytes to test > 1 line (general case).
// Use all hex digits in both nibbles to test hex formatting.
private static final byte[] TEST_DATA = new byte[] {
(byte) 0xF0, (byte) 0xE1, (byte) 0xD2, (byte) 0xC3,
(byte) 0xB4, (byte) 0xA5, (byte) 0x96, (byte) 0x87,
(byte) 0x78, (byte) 0x69, (byte) 0x5A, (byte) 0x4B,
(byte) 0x3C, (byte) 0x2D, (byte) 0x1E, (byte) 0x0F,
};
StringWriter writer = new StringWriter();
CppMetadataGenerator.emitStaticArrayData(new PrintWriter(writer), data);
}
};
private static final int TEST_DATA_LEN = TEST_DATA.length;
private static final String TEST_CONSTANT_NAME = "METADATA";
@Test
public void outputHeaderFile() throws IOException {
byte[] data = new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
CppMetadataGenerator metadata = CppMetadataGenerator.create(Type.METADATA, data);
public void emitStaticArrayData() {
byte[] data = TEST_DATA;
StringWriter writer = new StringWriter();
metadata.outputHeaderFile(writer);
Iterator<String> lines = toLines(writer.toString()).iterator();
// Sanity check that at least some of the expected lines are present.
assertTrue(consumeUntil(" * Copyright (C) 2011 The Libphonenumber Authors", lines));
assertTrue(consumeUntil("#ifndef I18N_PHONENUMBERS_METADATA_H_", lines));
assertTrue(consumeUntil("#define I18N_PHONENUMBERS_METADATA_H_", lines));
assertTrue(consumeUntil("namespace i18n {", lines));
assertTrue(consumeUntil("namespace phonenumbers {", lines));
assertTrue(consumeUntil("int metadata_size();", lines));
assertTrue(consumeUntil("const void* metadata_get();", lines));
assertTrue(consumeUntil("#endif // I18N_PHONENUMBERS_METADATA_H_", lines));
RustMetadataGenerator.emitStaticArrayData(new PrintWriter(writer), data);
}
@Test
public void outputSourceFile() throws IOException {
byte[] data = new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
CppMetadataGenerator metadata = CppMetadataGenerator.create(Type.ALTERNATE_FORMAT, data);
String testDataLen = String.valueOf(data.length);
RustMetadataGenerator metadata = RustMetadataGenerator.create(Type.ALTERNATE_FORMAT, data, TEST_CONSTANT_NAME);
StringWriter writer = new StringWriter();
metadata.outputSourceFile(writer);
Iterator<String> lines = toLines(writer.toString()).iterator();
// Sanity check that at least some of the expected lines are present.
assertTrue(consumeUntil(" * Copyright (C) 2012 The Libphonenumber Authors", lines));
assertTrue(consumeUntil("namespace i18n {", lines));
assertTrue(consumeUntil("namespace phonenumbers {", lines));
assertTrue(consumeUntil("namespace {", lines));
assertTrue(consumeUntil("static const unsigned char data[] = {", lines));
assertTrue(consumeUntil("pub const "+TEST_CONSTANT_NAME+": [u8; "+testDataLen+"] = [", lines));
assertTrue(consumeUntil(" 0xCA, 0xFE, 0xBA, 0xBE", lines));
assertTrue(consumeUntil("int alternate_format_size() {", lines));
assertTrue(consumeUntil("const void* alternate_format_get() {", lines));
assertTrue(consumeUntil("];", lines));
}
/** Converts a string containing newlines into a list of lines. */