/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/* $Id$ */
package org.apache.fop.complexscripts.bidi;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* <p>Test case for Unicode Bidi Algorithm.</p>
*/
public class BidiAlgorithmTestCase {
/**
* logging instance
*/
private static final Log log = LogFactory.getLog(BidiAlgorithmTestCase.class);
/**
* Concatenated array of <test-set,test-sequence> tuples
* specifying which sequences are to be excluded from testing,
* where -1 for either component is a wildcard.
*/
private static final int[] EXCLUSIONS = {
// no exclusions
};
/**
* Concatenated array of <test-set,test-sequence> tuples
* specifying which sequences are to be included in testing, where
* -1 for either component is a wildcard.
*/
private static final int[] INCLUSIONS = {
-1, -1 // all sequences
};
/**
* Concatenated array of <start,end> tuples expressing ranges of
* test sets to be tested, where -1 in the end position signifies
* all remaining test sets.
*/
private static final int[] TEST_SET_RANGES = {
0, -1 // all test sets
};
// instrumentation
private int includedSequences;
private int excludedSequences;
private int passedSequences;
@Test
public void testBidiAlgorithm() throws Exception {
String ldPfx = BidiTestData.LD_PFX;
int ldCount = BidiTestData.LD_CNT;
for (int i = 0; i < ldCount; i++) {
int[] da = BidiTestData.readTestData(ldPfx, i);
if (da != null) {
testBidiAlgorithm(i, da);
} else {
fail("unable to read bidi test data for resource at index " + i);
}
}
// ensure we passed all test sequences
assertEquals("did not pass all test sequences", BidiTestData.NUM_TEST_SEQUENCES, passedSequences);
if (log.isDebugEnabled()) {
log.debug("Included Sequences : " + includedSequences);
log.debug("Excluded Sequences : " + excludedSequences);
log.debug("Passed Sequences : " + passedSequences);
}
}
private void testBidiAlgorithm(int testSet, int[] da) throws Exception {
if (da.length < 1) {
fail("test data is empty");
} else if (da.length < ((da[0] * 2) + 1)) {
fail("test data is truncated");
} else {
int k = 0;
// extract level count
int n = da[k++];
// extract level array
int[] la = new int [ n ];
for (int i = 0; i < n; i++) {
la[i] = da[k++];
}
// extract reorder array
int[] ra = new int [ n ];
for (int i = 0; i < n; i++) {
ra[i] = da[k++];
}
// extract and test each test sequence
int testSequence = 0;
int[] ta = new int [ n ];
while ((k + (1 + n)) <= da.length) {
int bs = da[k++];
for (int i = 0; i < n; i++) {
ta[i] = da[k++];
}
if (includeSequence(testSet, testSequence)) {
includedSequences++;
if (!excludeSequence(testSet, testSequence)) {
if (testBidiAlgorithm(testSet, testSequence, la, ra, ta, bs)) {
passedSequences++;
}
} else {
excludedSequences++;
}
}
testSequence++;
}
// ensure we exhausted test data
assertEquals("extraneous test data", da.length, k);
}
}
private boolean includeTestSet(int testSet) {
for (int i = 0, n = TEST_SET_RANGES.length / 2; i < n; i++) {
int s = TEST_SET_RANGES [ (i * 2) + 0 ];
int e = TEST_SET_RANGES [ (i * 2) + 1 ];
if (testSet >= s) {
if ((e < 0) || (testSet <= e)) {
return true;
}
}
}
return false;
}
private boolean includeSequence(int testSet, int testSequence) {
if (!includeTestSet(testSet)) {
return false;
} else {
for (int i = 0, n = INCLUSIONS.length / 2; i < n; i++) {
int setno = INCLUSIONS [ (i * 2) + 0 ];
int seqno = INCLUSIONS [ (i * 2) + 1 ];
if (setno < 0) {
if (seqno < 0) {
return true;
} else if (seqno == testSequence) {
return true;
}
} else if (setno == testSet) {
if (seqno < 0) {
return true;
} else if (seqno == testSequence) {
return true;
}
}
}
return false;
}
}
private boolean excludeSequence(int testSet, int testSequence) {
for (int i = 0, n = EXCLUSIONS.length / 2; i < n; i++) {
int setno = EXCLUSIONS [ (i * 2) + 0 ];
int seqno = EXCLUSIONS [ (i * 2) + 1 ];
if (setno < 0) {
if (seqno < 0) {
return true;
} else if (seqno == testSequence) {
return true;
}
} else if (setno == testSet) {
if (seqno < 0) {
return true;
} else if (seqno == testSequence) {
return true;
}
}
}
return false;
}
private boolean testBidiAlgorithm(int testSet, int testSequence, int[] la, int[] ra, int[] ta, int bs)
throws Exception {
boolean passed = true;
int n = la.length;
if (ra.length != n) {
fail("bad reorder array length, expected " + n + ", got " + ra.length);
} else if (ta.length != n) {
fail("bad test array length, expected " + n + ", got " + ta.length);
} else {
// auto-LTR
if ((bs & 1) != 0) {
// auto-LTR is performed at higher level
}
// LTR
if ((bs & 2) != 0) {
int[] levels = UnicodeBidiAlgorithm.resolveLevels(null, ta, 0, new int [ n ], true);
if (!verifyResults(la, levels, ta, 0, testSet, testSequence)) {
passed = false;
}
}
// RTL
if ((bs & 4) != 0) {
int[] levels = UnicodeBidiAlgorithm.resolveLevels(null, ta, 1, new int [ n ], true);
if (!verifyResults(la, levels, ta, 1, testSet, testSequence)) {
passed = false;
}
}
}
return passed;
}
private boolean verifyResults(int[] laExp, int[] laOut, int[] ta, int dl, int testSet, int testSequence) {
if (laOut.length != laExp.length) {
fail("output levels array length mismatch, expected " + laExp.length + ", got " + laOut.length);
return false;
} else {
int numMatch = 0;
for (int i = 0, n = laExp.length; i < n; i++) {
if (laExp[i] >= 0) {
int lo = laOut[i];
int le = laExp[i];
if (lo != le) {
assertEquals(getMismatchMessage(testSet, testSequence, i, dl), le, lo);
} else {
numMatch++;
}
} else {
numMatch++;
}
}
return numMatch == laExp.length;
}
}
private String getMismatchMessage(int testSet, int testSequence, int seqIndex, int defaultLevel) {
StringBuffer sb = new StringBuffer();
sb.append("level mismatch for default level ");
sb.append(defaultLevel);
sb.append(" at sequence index ");
sb.append(seqIndex);
sb.append(" in test sequence ");
sb.append(testSequence);
sb.append(" of test set ");
sb.append(testSet);
return sb.toString();
}
}