/**
 * Copyright (c) 2013 Source Auditor Inc.
 * Copyright (c) 2013 Black Duck Software Inc.
 *
 *   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
 *
 *       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.
 *
*/
package org.spdx.compare;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.spdx.compare.SpdxComparer.SPDXReviewDifference;
import org.spdx.rdfparser.InvalidSPDXAnalysisException;
import org.spdx.rdfparser.SPDXCreatorInformation;
import org.spdx.rdfparser.SPDXDocumentFactory;
import org.spdx.rdfparser.SPDXReview;
import org.spdx.rdfparser.SpdxDocumentContainer;
import org.spdx.rdfparser.SpdxPackageVerificationCode;
import org.spdx.rdfparser.SpdxRdfConstants;
import org.spdx.rdfparser.license.AnyLicenseInfo;
import org.spdx.rdfparser.license.ConjunctiveLicenseSet;
import org.spdx.rdfparser.license.DisjunctiveLicenseSet;
import org.spdx.rdfparser.license.DuplicateExtractedLicenseIdException;
import org.spdx.rdfparser.license.ExtractedLicenseInfo;
import org.spdx.rdfparser.license.License;
import org.spdx.rdfparser.license.LicenseInfoFactory;
import org.spdx.rdfparser.license.SpdxListedLicense;
import org.spdx.rdfparser.license.SpdxNoAssertionLicense;
import org.spdx.rdfparser.license.SpdxNoneLicense;
import org.spdx.rdfparser.model.Annotation;
import org.spdx.rdfparser.model.Annotation.AnnotationType;
import org.spdx.rdfparser.model.Checksum;
import org.spdx.rdfparser.model.Checksum.ChecksumAlgorithm;
import org.spdx.rdfparser.model.ExternalDocumentRef;
import org.spdx.rdfparser.model.Relationship;
import org.spdx.rdfparser.model.Relationship.RelationshipType;
import org.spdx.rdfparser.model.SpdxDocument;
import org.spdx.rdfparser.model.SpdxElement;
import org.spdx.rdfparser.model.SpdxFile;
import org.spdx.rdfparser.model.SpdxFile.FileType;
import org.spdx.rdfparser.model.SpdxSnippet;
import org.spdx.rdfparser.model.pointer.ByteOffsetPointer;
import org.spdx.rdfparser.model.pointer.LineCharPointer;
import org.spdx.rdfparser.model.pointer.StartEndPointer;
import org.spdx.rdfparser.model.SpdxItem;
import org.spdx.rdfparser.model.SpdxPackage;
import org.spdx.rdfparser.model.UnitTestHelper;
import org.spdx.spdxspreadsheet.InvalidLicenseStringException;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * @author Gary O'Neall
 *
 */
public class SpdxComparerTest {

	static final String TEST_RDF_FILE_PATH = "TestFiles"+File.separator+"SPDXRdfExample.rdf";
	private static final String STD_LIC_ID_CC0 = "CC-BY-1.0";
	private static final String STD_LIC_ID_MPL11 = "MPL-1.1";
	File testRDFFile;

	private static final String COMMENTA = "comment A";
	private static final String COMMENTB = "comment B";
	private static final ExtractedLicenseInfo LICENSEA1 = new ExtractedLicenseInfo("LicenseRef-1", "License1");
	private static final ExtractedLicenseInfo LICENSEA2 = new ExtractedLicenseInfo("LicenseRef-2", "License2");
	private static final ExtractedLicenseInfo LICENSEA3 = new ExtractedLicenseInfo("LicenseRef-3", "License3");
	private static final ExtractedLicenseInfo LICENSEB1 = new ExtractedLicenseInfo("LicenseRef-4", "License1");
	private static final ExtractedLicenseInfo LICENSEB2 = new ExtractedLicenseInfo("LicenseRef-5", "License2");
	private static final ExtractedLicenseInfo LICENSEB3 = new ExtractedLicenseInfo("LicenseRef-6", "License3");
	private static final AnyLicenseInfo[] LICENSE_INFO_FROM_FILESA = new AnyLicenseInfo[] {LICENSEA1, LICENSEA2, LICENSEA3};
	private static final AnyLicenseInfo[] LICENSE_INFO_FROM_FILESB = new AnyLicenseInfo[] {LICENSEB1, LICENSEB2, LICENSEB3};
	private static final String LICENSE_COMMENTA = "License Comment A";
	@SuppressWarnings("unused")
	private static final String LICENSE_COMMENTB = "License Comment B";
	private static final String COPYRIGHTA = "Copyright A";
	@SuppressWarnings("unused")
	private static final String COPYRIGHTB = "Copyright B";
	private static final AnyLicenseInfo LICENSE_CONCLUDEDA = LICENSEA1;
	private static final AnyLicenseInfo LICENSE_CONCLUDEDB = LICENSEB1;
	private static final String NAMEA = "NameA";
	private static final String NAMEB = "NameB";
	private static final String NAMEC = "NameC";
	private static final Map<String, String> LICENSE_XLATION_MAP = Maps.newHashMap();
	private static final AnyLicenseInfo LICENSE_DECLAREDA = LICENSEA2;
	private static final AnyLicenseInfo LICENSE_DECLAREDB = LICENSEB2;
	private static final String ORIGINATORA = "Organization: OrgA";
	@SuppressWarnings("unused")
	private static final String ORIGINATORB = "Organization: OrgB";
	private static final String HOMEPAGEA = "http://home.page/a";
	@SuppressWarnings("unused")
	private static final String HOMEPAGEB = "http://home.page/b";
	private static final String DOWNLOADA = "http://download.page/a";
	@SuppressWarnings("unused")
	private static final String DOWNLOADB = "http://download.page/b";
	private static final String DESCRIPTIONA = "Description A";
	@SuppressWarnings("unused")
	private static final String DESCRIPTIONB = "Description B";
	private static final String PACKAGE_FILENAMEA = "packageFileNameA";
	@SuppressWarnings("unused")
	private static final String PACKAGE_FILENAMEB = "packageFileNameB";
	private static final String SOURCEINFOA = "Sourc info A";
	@SuppressWarnings("unused")
	private static final String SOURCEINFOB = "Sourc info B";
	private static final String SUMMARYA = "Summary A";
	@SuppressWarnings("unused")
	private static final String SUMMARYB = "Summary B";
	private static final String VERSIONINFOA = "Version A";
	@SuppressWarnings("unused")
	private static final String VERSIONINFOB = "Version B";
	private static final String SUPPLIERA = "Person: Supplier A";
	@SuppressWarnings("unused")
	private static final String SUPPLIERB = "Person: Supplier B";
	private static final String DOC_URIA = "http://spdx.org/documents/uriA";
	private static final String DOC_URIB = "http://spdx.org/documents/uriB";
	private static final String DOC_URIC = "http://spdx.org/documents/uriC";
	private static final ExtractedLicenseInfo[] EXTRACTED_LICENSESA = new ExtractedLicenseInfo[]{
		LICENSEA1, LICENSEA2, LICENSEA3
	};
	private static final ExtractedLicenseInfo[] EXTRACTED_LICENSESB = new ExtractedLicenseInfo[]{
		LICENSEB1, LICENSEB2, LICENSEB3
	};
	private static final String DOC_NAMEA = "DocumentA";
	private static final String DOC_NAMEB = "DocumentB";
	private static final String DOC_NAMEC = "DocumentC";
	private static final SPDXCreatorInformation CREATION_INFOA = new SPDXCreatorInformation(
			new String[] {"Person: CreatorA"}, "2010-01-29T18:30:22Z", "Creator CommentA", "1.15");
	private static final SPDXCreatorInformation CREATION_INFOB = new SPDXCreatorInformation(
			new String[] {"Person: CreatorB"}, "2012-01-29T18:30:22Z", "Creator CommentB", "1.17");
	static {
		LICENSE_XLATION_MAP.put("LicenseRef-1", "LicenseRef-4");
		LICENSE_XLATION_MAP.put("LicenseRef-2", "LicenseRef-5");
		LICENSE_XLATION_MAP.put("LicenseRef-3", "LicenseRef-6");
	}

	Integer OFFSET1_1 = new Integer(2342);
	ByteOffsetPointer BOP_POINTER1_1;
	Integer LINE1_1 = new Integer(113);
	LineCharPointer LCP_POINTER1_1;
	Integer OFFSET2_1 = new Integer(444);
	ByteOffsetPointer BOP_POINTER2_1;
	Integer LINE2_1 = new Integer(23422);
	LineCharPointer LCP_POINTER2_1;
	Integer OFFSET1_2 = new Integer(3542);
	ByteOffsetPointer BOP_POINTER1_2;
	Integer LINE1_2 = new Integer(555);
	LineCharPointer LCP_POINTER1_2;
	Integer OFFSET2_2 = new Integer(2444);
	ByteOffsetPointer BOP_POINTER2_2;
	Integer LINE2_2 = new Integer(23428);

	private Annotation ANNOTATION1;
	private Annotation ANNOTATION2;
	private Annotation ANNOTATION3;
	private Annotation ANNOTATION4;
	private Annotation[] ANNOTATIONSA;
	private Annotation[] ANNOTATIONSB;

	private Relationship[] RELATIONSHIPSA;
	private Relationship[] RELATIONSHIPSB;
	private SpdxElement RELATED_ELEMENT1;
	private SpdxElement RELATED_ELEMENT2;
	private SpdxElement RELATED_ELEMENT3;
	private SpdxElement RELATED_ELEMENT4;
	private Relationship RELATIONSHIP1;
	private Relationship RELATIONSHIP2;
	private Relationship RELATIONSHIP3;
	private Relationship RELATIONSHIP4;
	private Checksum CHECKSUM1;
	private Checksum CHECKSUM2;
	private Checksum CHECKSUM3;
	private Checksum CHECKSUM4;
	private Checksum[] CHECKSUMSA;
	private Checksum[] CHECKSUMSB;
	private String FILE1_NAME = "file1Name";
	private String FILE2_NAME = "file2Name";
	private String FILE3_NAME = "file3Name";
	private String FILE4_NAME = "file4Name";
	private SpdxFile FILE1A;
	private SpdxFile FILE1B;
	private SpdxFile FILE1B_DIFF_CHECKSUM;
	private SpdxFile FILE2A;
	private SpdxFile FILE3A;
	private SpdxFile FILE2B;
	private SpdxFile FILE3B;
	private SpdxFile FILE4A;
	private SpdxFile[] FILESA;
	private SpdxFile[] FILESB;
	private SpdxFile[] FILESB_SAME;
	private SpdxPackageVerificationCode VERIFICATION_CODEA;
	@SuppressWarnings("unused")
	private SpdxPackageVerificationCode VERIFICATION_CODEB;

	LineCharPointer LCP_POINTER2_2;
	StartEndPointer BYTE_RANGE1;
	StartEndPointer BYTE_RANGE2;
	StartEndPointer LINE_RANGE1;
	StartEndPointer LINE_RANGE2;

	SpdxSnippet SNIPPET1;
	SpdxSnippet SNIPPET2;

	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		this.testRDFFile = new File(TEST_RDF_FILE_PATH);
		ANNOTATION1 = new Annotation("Annotator1", AnnotationType.annotationType_other,
				"2010-01-29T18:30:22Z", "AnnotationComment1");
		ANNOTATION2 = new Annotation("Annotator2", AnnotationType.annotationType_review,
				"2011-01-29T18:30:22Z", "AnnotationComment2");
		ANNOTATION3 = new Annotation("Annotator3", AnnotationType.annotationType_other,
				"2012-01-29T18:30:22Z", "AnnotationComment3");
		ANNOTATION4 = new Annotation("Annotator4", AnnotationType.annotationType_review,
				"2013-01-29T18:30:22Z", "AnnotationComment4");
		ANNOTATIONSA = new Annotation[] {ANNOTATION1, ANNOTATION2};
		ANNOTATIONSB = new Annotation[] {ANNOTATION3, ANNOTATION4};
		RELATED_ELEMENT1 = new SpdxElement("relatedElementName1",
				"related element comment 1", null, null);
		RELATED_ELEMENT2 = new SpdxElement("relatedElementName2",
				"related element comment 2", null, null);
		RELATED_ELEMENT3 = new SpdxElement("relatedElementName3",
				"related element comment 3", null, null);
		RELATED_ELEMENT4 = new SpdxElement("relatedElementName4",
				"related element comment 4", null, null);
		RELATIONSHIP1 = new Relationship(RELATED_ELEMENT1,
				RelationshipType.CONTAINS, "Relationship Comment1");
		RELATIONSHIP2 = new Relationship(RELATED_ELEMENT2,
				RelationshipType.DYNAMIC_LINK, "Relationship Comment2");
		RELATIONSHIP3 = new Relationship(RELATED_ELEMENT3,
				RelationshipType.DATA_FILE_OF, "Relationship Comment3");
		RELATIONSHIP4 = new Relationship(RELATED_ELEMENT4,
				RelationshipType.DISTRIBUTION_ARTIFACT, "Relationship Comment4");
		RELATIONSHIPSA = new Relationship[] {RELATIONSHIP1, RELATIONSHIP2};
		RELATIONSHIPSB = new Relationship[] {RELATIONSHIP3, RELATIONSHIP4};
		CHECKSUM1 = new Checksum(ChecksumAlgorithm.checksumAlgorithm_sha1,
				"111bf72bf99b7e471f1a27989667a903658652bb");
		CHECKSUM2 = new Checksum(ChecksumAlgorithm.checksumAlgorithm_sha1,
				"222bf72bf99b7e471f1a27989667a903658652bb");
		CHECKSUM3 = new Checksum(ChecksumAlgorithm.checksumAlgorithm_sha1,
				"333bf72bf99b7e471f1a27989667a903658652bb");
		CHECKSUM4 = new Checksum(ChecksumAlgorithm.checksumAlgorithm_sha1,
				"444bf72bf99b7e471f1a27989667a903658652bb");
		CHECKSUMSA = new Checksum[] {CHECKSUM1, CHECKSUM2};
		CHECKSUMSB = new Checksum[] {CHECKSUM3, CHECKSUM4};

		FILE1A = new SpdxFile(FILE1_NAME, null, null, null,
				LICENSE_CONCLUDEDA, new AnyLicenseInfo[] {LICENSEA1, LICENSEA2},
				null, null, new FileType[] {FileType.fileType_documentation, FileType.fileType_text},
				CHECKSUMSA, null, null, null);
		FILE1B = new SpdxFile(FILE1_NAME, null, null, null,
				LICENSE_CONCLUDEDB, new AnyLicenseInfo[] {LICENSEB1, LICENSEB2},
				null, null, new FileType[] {FileType.fileType_documentation, FileType.fileType_text},
				CHECKSUMSA, null, null, null);
		FILE1B_DIFF_CHECKSUM = new SpdxFile(FILE1_NAME, null, null, null,
				LICENSE_CONCLUDEDB, new AnyLicenseInfo[] {LICENSEB1, LICENSEB2},
				null, null, new FileType[] {FileType.fileType_documentation, FileType.fileType_text},
				CHECKSUMSB, null, null, null);
		FILE2A = new SpdxFile(FILE2_NAME, null, null, null,
				LICENSE_CONCLUDEDA, new AnyLicenseInfo[] {LICENSEA1, LICENSEA2},
				null, null, new FileType[] {FileType.fileType_documentation, FileType.fileType_text},
				CHECKSUMSA, null, null, null);
		FILE3A = new SpdxFile(FILE3_NAME, null, null, null,
				LICENSE_CONCLUDEDA, new AnyLicenseInfo[] {LICENSEA1, LICENSEA2},
				null, null, new FileType[] {FileType.fileType_documentation, FileType.fileType_text},
				CHECKSUMSA, null, null, null);
		FILE4A = new SpdxFile(FILE4_NAME, null, null, null,
				LICENSE_CONCLUDEDA, new AnyLicenseInfo[] {LICENSEA1, LICENSEA2},
				null, null, new FileType[] {FileType.fileType_documentation, FileType.fileType_text},
				CHECKSUMSA, null, null, null);
		FILE2B = new SpdxFile(FILE2_NAME, null, null, null,
				LICENSE_CONCLUDEDB, new AnyLicenseInfo[] {LICENSEB1, LICENSEB2},
				null, null, new FileType[] {FileType.fileType_documentation, FileType.fileType_text},
				CHECKSUMSA, null, null, null);
		FILE3B = new SpdxFile(FILE3_NAME, null, null, null,
				LICENSE_CONCLUDEDB, new AnyLicenseInfo[] {LICENSEB1, LICENSEB2},
				null, null, new FileType[] {FileType.fileType_documentation, FileType.fileType_text},
				CHECKSUMSA, null, null, null);

		FILESA = new SpdxFile[] {FILE1A, FILE2A};
		FILESB_SAME = new SpdxFile[] {FILE1B, FILE2B};
		FILESB = new SpdxFile[] {FILE1B_DIFF_CHECKSUM, FILE3B};
		VERIFICATION_CODEA = new SpdxPackageVerificationCode("aaabf72bf99b7e471f1a27989667a903658652bb",
				new String[] {"file2"});
		VERIFICATION_CODEB = new SpdxPackageVerificationCode("bbbbf72bf99b7e471f1a27989667a903658652bb",
				new String[] {"file3"});
		BOP_POINTER1_1 = new ByteOffsetPointer(FILE1A, OFFSET1_1);
		BOP_POINTER1_2 = new ByteOffsetPointer(FILE1A, OFFSET1_2);
		BYTE_RANGE1 = new StartEndPointer(BOP_POINTER1_1, BOP_POINTER1_2);
		LCP_POINTER1_1 = new LineCharPointer(FILE1A, LINE1_1);
		LCP_POINTER1_2 = new LineCharPointer(FILE1A, LINE1_2);
		LINE_RANGE1 = new StartEndPointer(LCP_POINTER1_1, LCP_POINTER1_2);
		BOP_POINTER2_1 = new ByteOffsetPointer(FILE2A, OFFSET2_1);
		BOP_POINTER2_2 = new ByteOffsetPointer(FILE2A, OFFSET2_2);
		BYTE_RANGE2 = new StartEndPointer(BOP_POINTER2_1, BOP_POINTER2_2);
		LCP_POINTER2_1 = new LineCharPointer(FILE2A, LINE2_1);
		LCP_POINTER2_2 = new LineCharPointer(FILE2A, LINE2_2);
		LINE_RANGE2 = new StartEndPointer(LCP_POINTER2_1, LCP_POINTER2_2);
		SNIPPET1 = new SpdxSnippet("SnippetName1", "SnippetCOmment1", ANNOTATIONSA,
				RELATIONSHIPSA, LICENSE_CONCLUDEDA, new AnyLicenseInfo[] {LICENSEA1, LICENSEA2},
				"Copyright1", "LicenseComment1", FILE1A, BYTE_RANGE1, LINE_RANGE1);
		SNIPPET2 = new SpdxSnippet("SnippetName2", "SnippetCOmment2", ANNOTATIONSB,
				RELATIONSHIPSB, LICENSE_CONCLUDEDB, new AnyLicenseInfo[] {LICENSEB1, LICENSEB2},
				"Copyright2", "LicenseComment2", FILE2A, BYTE_RANGE2, LINE_RANGE2);
	}

	/**
	 * @throws java.lang.Exception
	 */
	@After
	public void tearDown() throws Exception {
	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#compare(org.spdx.rdfparser.SpdxDocument, org.spdx.rdfparser.SpdxDocument)}.
	 * @throws InvalidSPDXAnalysisException
	 * @throws IOException
	 * @throws SpdxCompareException
	 */
	@Test
	public void testCompare() throws IOException, InvalidSPDXAnalysisException, SpdxCompareException {
		SpdxComparer comparer = new SpdxComparer();
		SpdxDocument doc1 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		SpdxDocument doc2 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		try {
			comparer.isCreatorInformationEqual();	// should fail
			fail("Not checking for comparer being complete");
		} catch (SpdxCompareException ex) {
			// we expect an error
		}
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isDifferenceFound());
		assertEquals(doc1.hashCode(), comparer.getSpdxDoc(0).hashCode());
		assertEquals(doc2.hashCode(), comparer.getSpdxDoc(1).hashCode());
	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#compareLicense(int, org.spdx.rdfparser.license.AnyLicenseInfo, int, org.spdx.rdfparser.license.AnyLicenseInfo)}.
	 * @throws InvalidSPDXAnalysisException
	 * @throws IOException
	 * @throws SpdxCompareException
	 * @throws InvalidLicenseStringException
	 */
	@Test
	public void testCompareLicense() throws IOException, InvalidSPDXAnalysisException, SpdxCompareException, InvalidLicenseStringException {
		SpdxComparer comparer = new SpdxComparer();
		SpdxDocument doc1 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		SpdxDocument doc2 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		alterExtractedLicenseInfoIds(doc2, 1);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isDifferenceFound());
		ExtractedLicenseInfo[] extractedInfos1 = doc1.getExtractedLicenseInfos();
		ExtractedLicenseInfo[] extractedInfos2 = doc2.getExtractedLicenseInfos();

		Map<Integer, Integer> xlateDoc1ToDoc2LicId = createLicIdXlation(extractedInfos1, extractedInfos2);

		//Standard License
		SpdxListedLicense lic1 = LicenseInfoFactory.getListedLicenseById(STD_LIC_ID_CC0);
		SpdxListedLicense lic1_1 = LicenseInfoFactory.getListedLicenseById(STD_LIC_ID_CC0);
		SpdxListedLicense lic2 = LicenseInfoFactory.getListedLicenseById(STD_LIC_ID_MPL11);
		assertTrue(comparer.compareLicense(0, lic1, 1, lic1_1));
		assertFalse(comparer.compareLicense(0, lic1, 1, lic2));
		//Extracted License
		assertTrue(comparer.compareLicense(0, extractedInfos1[0], 1, extractedInfos2[xlateDoc1ToDoc2LicId.get(0)]));
		int nonEqual = 0;
		if (xlateDoc1ToDoc2LicId.get(0) == nonEqual) {
			nonEqual = 1;
		}
		assertFalse(comparer.compareLicense(0, extractedInfos1[0], 1, extractedInfos2[nonEqual]));
		try {
			assertFalse(comparer.compareLicense(0, extractedInfos1[0], 1, extractedInfos1[0]));
		} catch(SpdxCompareException ex) {
			// we expect a mappint exception
		}
		//Conjunctive License
		StringBuilder sb = new StringBuilder("(");
		sb.append(STD_LIC_ID_CC0);
		sb.append(" AND ");
		sb.append(extractedInfos1[0].getLicenseId());
		sb.append(" AND ");
		sb.append(STD_LIC_ID_MPL11);
		sb.append(" AND ");
		sb.append(extractedInfos1[1].getLicenseId());
		sb.append(")");
		AnyLicenseInfo conj1 = LicenseInfoFactory.parseSPDXLicenseString(sb.toString());

		sb = new StringBuilder("(");
		sb.append(STD_LIC_ID_MPL11);
		sb.append(" AND ");
		sb.append(extractedInfos2[xlateDoc1ToDoc2LicId.get(1)].getLicenseId());
		sb.append(" AND ");
		sb.append(STD_LIC_ID_CC0);
		sb.append(" AND ");
		sb.append(extractedInfos2[xlateDoc1ToDoc2LicId.get(0)].getLicenseId());
		sb.append(")");
		AnyLicenseInfo conj2 = LicenseInfoFactory.parseSPDXLicenseString(sb.toString());

		sb = new StringBuilder("(");
		sb.append(STD_LIC_ID_MPL11);
		sb.append(" AND ");
		sb.append(extractedInfos2[xlateDoc1ToDoc2LicId.get(1)].getLicenseId());
		sb.append(" AND ");
		sb.append(STD_LIC_ID_CC0);
		sb.append(")");
		AnyLicenseInfo conj3 = LicenseInfoFactory.parseSPDXLicenseString(sb.toString());

		assertTrue(comparer.compareLicense(0, conj1, 1, conj2));
		assertFalse(comparer.compareLicense(0, conj1, 1, conj3));
		try {
			assertFalse(comparer.compareLicense(0, conj2, 1, conj2));
		} catch(SpdxCompareException ex) {
			// we expect a mappint exception
		}
		//Disjunctive License
		sb = new StringBuilder("(");
		sb.append(STD_LIC_ID_CC0);
		sb.append(" OR ");
		sb.append(extractedInfos1[0].getLicenseId());
		sb.append(" OR ");
		sb.append(STD_LIC_ID_MPL11);
		sb.append(" OR ");
		sb.append(extractedInfos1[1].getLicenseId());
		sb.append(")");
		AnyLicenseInfo dis1 = LicenseInfoFactory.parseSPDXLicenseString(sb.toString());

		sb = new StringBuilder("(");
		sb.append(STD_LIC_ID_MPL11);
		sb.append(" OR ");
		sb.append(extractedInfos2[xlateDoc1ToDoc2LicId.get(1)].getLicenseId());
		sb.append(" OR ");
		sb.append(extractedInfos2[xlateDoc1ToDoc2LicId.get(0)].getLicenseId());
		sb.append(" OR ");
		sb.append(STD_LIC_ID_CC0);
		sb.append(")");
		AnyLicenseInfo dis2 = LicenseInfoFactory.parseSPDXLicenseString(sb.toString());

		sb = new StringBuilder("(");
		sb.append(STD_LIC_ID_MPL11);
		sb.append(" OR ");
		sb.append(extractedInfos2[xlateDoc1ToDoc2LicId.get(1)].getLicenseId());
		sb.append(" OR ");
		sb.append(STD_LIC_ID_CC0);
		sb.append(")");
		AnyLicenseInfo dis3 = LicenseInfoFactory.parseSPDXLicenseString(sb.toString());

		assertTrue(comparer.compareLicense(0, dis1, 1, dis2));
		assertFalse(comparer.compareLicense(0, dis1, 1, dis3));
		try {
			assertFalse(comparer.compareLicense(0, dis2, 1, dis2));
		} catch(SpdxCompareException ex) {
			// we expect a mappint exception
		}
		//Complex License
		DisjunctiveLicenseSet subcomplex1 = new DisjunctiveLicenseSet(
					new AnyLicenseInfo[] {lic1, conj1});
		ConjunctiveLicenseSet complex1 = new ConjunctiveLicenseSet(
				new AnyLicenseInfo[] {subcomplex1, dis1, extractedInfos1[0]});
		DisjunctiveLicenseSet subcomplex2 = new DisjunctiveLicenseSet(
				new AnyLicenseInfo[] {conj2, lic1_1});
		ConjunctiveLicenseSet complex2 = new ConjunctiveLicenseSet(
			new AnyLicenseInfo[] {dis2, subcomplex2, extractedInfos2[xlateDoc1ToDoc2LicId.get(0)]});

		DisjunctiveLicenseSet subcomplex3 = new DisjunctiveLicenseSet(
				new AnyLicenseInfo[] {conj3, lic1_1});
		ConjunctiveLicenseSet complex3 = new ConjunctiveLicenseSet(
			new AnyLicenseInfo[] {dis2, subcomplex3, extractedInfos2[xlateDoc1ToDoc2LicId.get(0)]});
		assertTrue(comparer.compareLicense(0, complex1, 1, complex2));
		assertFalse(comparer.compareLicense(0, complex1, 1, complex3));
		//NONE
		SpdxNoneLicense noneLic1 = new SpdxNoneLicense();
		SpdxNoneLicense noneLic2 = new SpdxNoneLicense();
		SpdxNoAssertionLicense noAssertLic1 = new SpdxNoAssertionLicense();
		SpdxNoAssertionLicense noAssertLic2 = new SpdxNoAssertionLicense();
		assertTrue (comparer.compareLicense(0, noneLic1, 1, noneLic2));
		assertFalse (comparer.compareLicense(0, complex1, 1, noneLic2));
		//NOASSERTION
		assertTrue(comparer.compareLicense(0, noAssertLic1, 1, noAssertLic2));
		assertFalse(comparer.compareLicense(0, noAssertLic2, 1, lic1));
		assertFalse(comparer.compareLicense(0, noneLic2, 1, noAssertLic1));
	}

	/**
	 * Create a license ID mapping table between licInfos1 and licInfos2 based on EXACT matches of text
	 * @param licInfos1
	 * @param licInfos2
	 * @return
	 */
	private Map<Integer, Integer> createLicIdXlation(
			ExtractedLicenseInfo[] licInfos1,
			ExtractedLicenseInfo[] licInfos2) {
		Map<Integer, Integer> retval = Maps.newHashMap();
		for (int i = 0;i < licInfos1.length; i++) {
			boolean found = false;
			for (int j = 0; j < licInfos2.length; j++) {
				if (licInfos1[i].getExtractedText().equals(licInfos2[j].getExtractedText())) {
					if (found) {
						fail("Two licenses found with the same text: "+licInfos1[i].getExtractedText());
					}
					retval.put(i, j);
				}
			}
		}
		return retval;
	}

	/**
	 * Modifies the extracted license info license ID's by adding a digit to
	 * each of the ID's
	 * @param doc
	 * @throws InvalidSPDXAnalysisException
	 */
	private void alterExtractedLicenseInfoIds(SpdxDocument doc, int digit) throws InvalidSPDXAnalysisException {
		ExtractedLicenseInfo[] extracted = doc.getExtractedLicenseInfos();
		for (int i = 0; i < extracted.length; i++) {
			String oldId = extracted[i].getLicenseId();
			String newId = oldId + String.valueOf(digit);
			extracted[i].setLicenseId(newId);
			assertEquals(0, extracted[i].verify().size());
		}

	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#isDifferenceFound()}.
	 * @throws InvalidSPDXAnalysisException
	 * @throws IOException
	 * @throws SpdxCompareException
	 */
	@Test
	public void testIsDifferenceFound() throws IOException, InvalidSPDXAnalysisException, SpdxCompareException {
		SpdxComparer comparer = new SpdxComparer();
		SpdxDocument doc1 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		SpdxDocument doc2 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		alterExtractedLicenseInfoIds(doc2, 1);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isDifferenceFound());
		doc2.setComment("a new doc comment");
		comparer.compare(doc1, doc2);
		assertTrue(comparer.isDifferenceFound());
		// Note - we will test the isDifferenceFound in each of the specific
		// differences unit tests below

	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#isSpdxVersionEqual()}.
	 * @throws InvalidSPDXAnalysisException
	 * @throws SpdxCompareException
	 * @throws IOException
	 */
	@Test
	public void testIsSpdxVersionEqual() throws InvalidSPDXAnalysisException, SpdxCompareException, IOException {
		SpdxComparer comparer = new SpdxComparer();
		SpdxDocument doc1 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		SpdxDocument doc2 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		alterExtractedLicenseInfoIds(doc2, 1);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isDifferenceFound());
		assertTrue(comparer.isSpdxVersionEqual());
		doc2.setSpecVersion("SPDX-1.0");
		comparer.compare(doc1, doc2);
		assertTrue(comparer.isDifferenceFound());
		assertFalse(comparer.isSpdxVersionEqual());
		doc1.setSpecVersion("SPDX-1.0");
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isDifferenceFound());
	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#getSpdxDoc(int)}.
	 * @throws InvalidSPDXAnalysisException
	 * @throws IOException
	 * @throws SpdxCompareException
	 */
	@Test
	public void testGetSpdxDoc() throws IOException, InvalidSPDXAnalysisException, SpdxCompareException {
		SpdxComparer comparer = new SpdxComparer();
		SpdxDocument doc1 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		SpdxDocument doc2 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		String DOC_2_COMMENT = "doc2";
		String DOC_1_COMMENT = "doc1";
		doc1.setComment(DOC_1_COMMENT);
		doc2.setComment(DOC_2_COMMENT);
		comparer.compare(doc1, doc2);
		assertEquals(DOC_1_COMMENT, comparer.getSpdxDoc(0).getComment());
		assertEquals(DOC_2_COMMENT, comparer.getSpdxDoc(1).getComment());

	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#isDataLicenseEqual()}.
	 * @throws InvalidSPDXAnalysisException
	 * @throws IOException
	 * @throws SpdxCompareException
	 */
	@Test
	public void testIsDataLicenseEqual() throws IOException, InvalidSPDXAnalysisException, SpdxCompareException {
		SpdxComparer comparer = new SpdxComparer();
		SpdxDocument doc1 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		SpdxDocument doc2 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		alterExtractedLicenseInfoIds(doc2, 1);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isDifferenceFound());
		assertTrue(comparer.isDataLicenseEqual());
		doc2.setSpecVersion("SPDX-1.0");
		doc2.setDataLicense(LicenseInfoFactory.getListedLicenseById(SpdxRdfConstants.SPDX_DATA_LICENSE_ID_VERSION_1_0));
		comparer.compare(doc1, doc2);
		assertTrue(comparer.isDifferenceFound());
		assertFalse(comparer.isDataLicenseEqual());
	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#isDocumentCommentsEqual()}.
	 */
	@Test
	public void testIsDocumentCommentsEqual()throws IOException, InvalidSPDXAnalysisException, SpdxCompareException {
		SpdxComparer comparer = new SpdxComparer();
		SpdxDocument doc1 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		SpdxDocument doc2 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		alterExtractedLicenseInfoIds(doc2, 1);
		comparer.compare(doc1, doc2);
		assertTrue(comparer.isDocumentCommentsEqual());
		assertFalse(comparer.isDifferenceFound());
		doc2.setComment("a new doc comment");
		comparer.compare(doc1, doc2);
		assertTrue(comparer.isDifferenceFound());
		assertFalse(comparer.isDocumentCommentsEqual());
		doc1.setComment("a new doc comment");
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isDifferenceFound());
		assertTrue(comparer.isDocumentCommentsEqual());
	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#isReviewersEqual()}.
	 */
	@SuppressWarnings("deprecation")
	@Test
	public void testIsReviewersEqual() throws IOException, InvalidSPDXAnalysisException, SpdxCompareException  {
		SpdxComparer comparer = new SpdxComparer();
		SpdxDocument doc1 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		SpdxDocument doc2 = SPDXDocumentFactory.createSpdxDocument(TEST_RDF_FILE_PATH);
		alterExtractedLicenseInfoIds(doc2, 1);

		String reviewDate1 = "2010-02-03T00:00:00Z";
		String reviewerComment1 = "comment1";
		String reviewer1 = "Person: creator1";
		SPDXReview review1 = new SPDXReview(reviewer1, reviewDate1, reviewerComment1);
		String reviewDate2 = "2010-02-03T10:00:00Z";
		String reviewerComment2 = "comment2";
		String reviewer2 = "Person: creator2";
		SPDXReview review2 = new SPDXReview(reviewer2, reviewDate2, reviewerComment2);
		String reviewDate3 = "2011-02-13T00:00:00Z";
		String reviewerComment3 = "comment3";
		String reviewer3 = "Person: creator3";
		SPDXReview review3 = new SPDXReview(reviewer3, reviewDate3, reviewerComment3);

		// empty reviewers
		SPDXReview[] emptyReview = new SPDXReview[0];
		doc1.setReviewers(emptyReview);
		doc2.setReviewers(emptyReview);
		comparer.compare(doc1, doc2);
		assertTrue(comparer.isReviewersEqual());
		assertFalse(comparer.isDifferenceFound());
		// one review
		SPDXReview[] oneReview = new SPDXReview[] {review1};
		doc1.setReviewers(oneReview);
		doc2.setReviewers(oneReview);
		comparer.compare(doc1, doc2);
		assertTrue(comparer.isReviewersEqual());
		assertFalse(comparer.isDifferenceFound());

		// empty reviewers and one more reviewer
		doc1.setReviewers(emptyReview);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isReviewersEqual());
		assertTrue(comparer.isDifferenceFound());
		doc1.setReviewers(emptyReview);
		doc2.setReviewers(oneReview);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isReviewersEqual());
		assertTrue(comparer.isDifferenceFound());

		// more reviewers
		SPDXReview[] twoReview = new SPDXReview[] {review1, review2};
		SPDXReview[] threeReview = new SPDXReview[] {review1, review2, review3};
		doc1.setReviewers(twoReview);
		doc2.setReviewers(threeReview);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isReviewersEqual());
		assertTrue(comparer.isDifferenceFound());

		// same reviewer names but different comment
		SPDXReview reviewThreeDiffComment = new SPDXReview(reviewer3, reviewDate3, "");
		SPDXReview[] threeReviewDiffComment = new SPDXReview[] {review1, review2, reviewThreeDiffComment};
		doc1.setReviewers(threeReviewDiffComment);
		doc2.setReviewers(threeReview);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isReviewersEqual());
		assertTrue(comparer.isDifferenceFound());

		// same reviewer names but different date
		SPDXReview review2DiffDate = new SPDXReview(reviewer2, "2012-02-03T10:00:00Z", reviewerComment2);
		SPDXReview[] threeReviewDiffDate = new SPDXReview[] {review1, review2, review2DiffDate};
		doc1.setReviewers(threeReview);
		doc2.setReviewers(threeReviewDiffDate);
		comparer.compare(doc1, doc2);
		assertFalse(comparer.isReviewersEqual());
		assertTrue(comparer.isDifferenceFound());

		// multiple reviewers different order
		SPDXReview[] threeReviewDifferentOrder = new SPDXReview[] {review2, review1, review3};
		doc1.setReviewers(threeReview);
		doc2.setReviewers(threeReviewDifferentOrder);
		comparer.compare(doc1, doc2);
		assertTrue(comparer.isReviewersEqual());
		assertFalse(comparer.isDifferenceFound());
	}

	/**
	 * Test method for {@link org.spdx.compare.SpdxComparer#isExtractedLicensingInfosEqual()}.
	 */
	@Test
	public void testIsExtractedLicensingInfosEqual() throws IOException, InvalidSPDXAnalysisException, SpdxCompareException  {
