package net.lingala.zip4j.headers;

import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.AESExtraDataRecord;
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.model.LocalFileHeader;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.AesKeyStrength;
import net.lingala.zip4j.model.enums.AesVersion;
import net.lingala.zip4j.model.enums.CompressionLevel;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.util.FileUtils;
import net.lingala.zip4j.util.InternalZipConstants;
import net.lingala.zip4j.util.RawIO;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.nio.charset.Charset;

import static net.lingala.zip4j.util.BitUtils.isBitSet;
import static net.lingala.zip4j.util.Zip4jUtil.epochToExtendedDosTime;
import static org.assertj.core.api.Assertions.assertThat;

public class FileHeaderFactoryTest {

  private static final String ACTUAL_OS = System.getProperty("os.name");
  private static final String FILE_NAME_IN_ZIP = "filename.txt";
  private static final long ENTRY_CRC = 2323L;

  private FileHeaderFactory fileHeaderFactory = new FileHeaderFactory();
  private RawIO rawIO = new RawIO();

  @Rule
  public ExpectedException expectedException = ExpectedException.none();

  @Before
  public void setup() {
    System.setProperty("os.name", "linux");
  }

  @After
  public void cleanup() {
    System.setProperty("os.name", ACTUAL_OS);
  }

  @Test
  public void testGenerateFileHeaderWithoutFileNameThrowsException() throws ZipException {
    expectedException.expect(ZipException.class);
    expectedException.expectMessage("fileNameInZip is null or empty");

    fileHeaderFactory.generateFileHeader(new ZipParameters(), false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
  }

  @Test
  public void testGenerateFileHeaderDefaults() throws ZipException {
    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(generateZipParameters(), false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);

    assertThat(fileHeader).isNotNull();
    assertThat(fileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.DEFLATE);
    verifyCompressionLevelGridForDeflate(CompressionLevel.NORMAL,
        fileHeader.getGeneralPurposeFlag()[0]);
    assertThat(fileHeader.isEncrypted()).isFalse();
    assertThat(fileHeader.getEncryptionMethod()).isEqualTo(EncryptionMethod.NONE);
    assertThat(fileHeader.getAesExtraDataRecord()).isNull();
    assertThat(fileHeader.getLastModifiedTime()).isNotZero();
    assertThat(fileHeader.getCompressedSize()).isEqualTo(0);
    assertThat(fileHeader.getUncompressedSize()).isEqualTo(0);
  }

  @Test
  public void testGenerateFileHeaderForStoreWithoutEncryption() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setCompressionMethod(CompressionMethod.STORE);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    verifyFileHeader(fileHeader, zipParameters, false, 0, 10, false);
  }

  @Test
  public void testGenerateFileHeaderWhenEncryptingWithoutMethodThrowsException() throws ZipException {
    expectedException.expect(ZipException.class);
    expectedException.expectMessage("Encryption method has to be set when encryptFiles flag is set in zip parameters");

    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);

    fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
  }

  @Test
  public void testGenerateFileHeaderWithStandardEncryption() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    verifyFileHeader(fileHeader, zipParameters, false, 0, 20, false);
  }

  @Test
  public void testGenerateFileHeaderWithAesEncryptionWithNullKeyStrengthThrowsException() throws ZipException {
    expectedException.expect(ZipException.class);
    expectedException.expectMessage("invalid AES key strength");

    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.AES);
    zipParameters.setAesKeyStrength(null);

    fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
  }

  @Test
  public void testGenerateFileHeaderWithAesEncryptionWithoutKeyStrengthUsesDefault() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.AES);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    verifyFileHeader(fileHeader, zipParameters, false, 0, 51, true);
    verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_256,
        CompressionMethod.DEFLATE, AesVersion.TWO);
  }

  @Test
  public void testGenerateFileHeaderWithAesEncryptionWithKeyStrength128() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.AES);
    zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_128);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    verifyFileHeader(fileHeader, zipParameters, false, 0, 51, true);
    verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_128,
        CompressionMethod.DEFLATE, AesVersion.TWO);
  }

  @Test
  public void testGenerateFileHeaderWithAesEncryptionWithKeyStrength192() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.AES);
    zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_192);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    verifyFileHeader(fileHeader, zipParameters, false, 0, 51, true);
    verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_192,
        CompressionMethod.DEFLATE, AesVersion.TWO);
  }

  @Test
  public void testGenerateFileHeaderWithAesEncryptionWithKeyStrength256() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.AES);
    zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    verifyFileHeader(fileHeader, zipParameters, false, 0, 51, true);
    verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_256,
        CompressionMethod.DEFLATE, AesVersion.TWO);
  }

  @Test
  public void testGenerateFileHeaderWithAesEncryptionVersionV1() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.AES);
    zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256);
    zipParameters.setAesVersion(AesVersion.ONE);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    verifyFileHeader(fileHeader, zipParameters, false, 0, 51, true);
    verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_256,
        CompressionMethod.DEFLATE, AesVersion.ONE);
  }

  @Test
  public void testGenerateFileHeaderWithAesEncryptionWithNullVersionUsesV2() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setEncryptFiles(true);
    zipParameters.setEncryptionMethod(EncryptionMethod.AES);
    zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256);
    zipParameters.setAesVersion(null);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    verifyFileHeader(fileHeader, zipParameters, false, 0, 51, true);
    verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_256,
        CompressionMethod.DEFLATE, AesVersion.TWO);
  }

  @Test
  public void testGenerateFileHeaderWithLastModifiedFileTime() throws ZipException {
    long lastModifiedFileTime = System.currentTimeMillis();
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setLastModifiedFileTime(lastModifiedFileTime);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);

    assertThat(fileHeader.getLastModifiedTime()).isEqualTo(epochToExtendedDosTime(zipParameters.getLastModifiedFileTime()));
  }

  @Test
  public void testGenerateFileHeaderWithCompressionLeveUltra() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setCompressionLevel(CompressionLevel.ULTRA);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);

    verifyCompressionLevelGridForDeflate(CompressionLevel.ULTRA, fileHeader.getGeneralPurposeFlag()[0]);
  }

  @Test
  public void testGenerateFileHeaderWithCompressionLevelMaximum() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setCompressionLevel(CompressionLevel.MAXIMUM);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);

    verifyCompressionLevelGridForDeflate(CompressionLevel.MAXIMUM, fileHeader.getGeneralPurposeFlag()[0]);
  }

  @Test
  public void testGenerateFileHeaderWithCompressionLevelFast() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setCompressionLevel(CompressionLevel.FAST);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);

    verifyCompressionLevelGridForDeflate(CompressionLevel.FAST, fileHeader.getGeneralPurposeFlag()[0]);
  }

  @Test
  public void testGenerateFileHeaderWithCompressionLevelFastest() throws ZipException {
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setCompressionLevel(CompressionLevel.FASTEST);

    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);

    verifyCompressionLevelGridForDeflate(CompressionLevel.FASTEST, fileHeader.getGeneralPurposeFlag()[0]);
  }

  @Test
  public void testGenerateFileHeaderWithCorrectCharset() throws ZipException {
    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(generateZipParameters(), false, 0, Charset.forName("Cp949"), rawIO);
    assertThat(isBitSet(fileHeader.getGeneralPurposeFlag()[1], 3)).isFalse();
  }

  @Test
  public void testGenerateFileHeaderWithUTF8Charset() throws ZipException {
    FileHeader fileHeader = fileHeaderFactory.generateFileHeader(generateZipParameters(), false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
    assertThat(isBitSet(fileHeader.getGeneralPurposeFlag()[1], 3)).isTrue();
  }

  @Test
  public void testGenerateLocalFileHeader() {
    long lastModifiedFileTime = epochToExtendedDosTime(System.currentTimeMillis());
    FileHeader  fileHeader = generateFileHeader(lastModifiedFileTime);

    LocalFileHeader localFileHeader = fileHeaderFactory.generateLocalFileHeader(fileHeader);

    verifyLocalFileHeader(localFileHeader, lastModifiedFileTime);
  }

  @Test
  public void testVersionMadeByWindowsWithUnixModeOff() {
    changeOsSystemPropertyToWindows();
    testVersionMadeBy(generateZipParameters(), 51);
  }

  @Test
  public void testVersionMadeByWindowsWithUnixModeOn() {
    changeOsSystemPropertyToWindows();
    ZipParameters zipParameters = generateZipParameters();
    zipParameters.setUnixMode(true);
    testVersionMadeBy(zipParameters, 819);
  }

  @Test
  public void testVersionMadeByUnix() {
    changeOsSystemPropertyToUnix();
    testVersionMadeBy(generateZipParameters(), 819);
  }

  @Test
  public void testVersionMadeByMac() {
    changeOsSystemPropertyToMac();
    testVersionMadeBy(generateZipParameters(), 819);
  }

  private void testVersionMadeBy(ZipParameters zipParameters, int expectedVersionMadeBy) {
    try {
      FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
      assertThat(fileHeader.getVersionMadeBy()).isEqualTo(expectedVersionMadeBy);
    } catch (Exception e) {
      restoreOsSystemProperty();
    }
  }

  private ZipParameters generateZipParameters() {
    ZipParameters zipParameters = new ZipParameters();
    zipParameters.setFileNameInZip(FILE_NAME_IN_ZIP);
    zipParameters.setEntryCRC(ENTRY_CRC);
    return zipParameters;
  }

  private FileHeader generateFileHeader(long lastModifiedFileTime) {
    FileHeader fileHeader = new FileHeader();
    fileHeader.setVersionNeededToExtract(20);
    fileHeader.setCompressionMethod(CompressionMethod.STORE);
    fileHeader.setLastModifiedTime(lastModifiedFileTime);
    fileHeader.setUncompressedSize(1000L);
    fileHeader.setFileName(FILE_NAME_IN_ZIP);
    fileHeader.setFileNameLength(FILE_NAME_IN_ZIP.getBytes(InternalZipConstants.CHARSET_UTF_8).length);
    fileHeader.setEncrypted(true);
    fileHeader.setEncryptionMethod(EncryptionMethod.AES);
    fileHeader.setCrc(1231231L);
    fileHeader.setCompressedSize(23523L);
    fileHeader.setGeneralPurposeFlag(new byte[] {2, 28});
    fileHeader.setDataDescriptorExists(true);
    fileHeader.setExtraFieldLength(190);
    return fileHeader;
  }

  private void verifyFileHeader(FileHeader fileHeader, ZipParameters zipParameters, boolean isSplitZip,
                                int diskNumberStart, int versionNeededToExtract, boolean aesExtraDataRecordPresent) {
    assertThat(fileHeader).isNotNull();
    assertThat(fileHeader.getSignature()).isEqualTo(HeaderSignature.CENTRAL_DIRECTORY);
    assertThat(fileHeader.getVersionMadeBy()).isEqualTo(819);
    assertThat(fileHeader.getVersionNeededToExtract()).isEqualTo(versionNeededToExtract);
    verifyCompressionMethod(fileHeader, zipParameters);
    assertThat(fileHeader.isEncrypted()).isEqualTo(zipParameters.isEncryptFiles());
    assertThat(fileHeader.getEncryptionMethod()).isEqualTo(zipParameters.isEncryptFiles()
        ? zipParameters.getEncryptionMethod() : EncryptionMethod.NONE);
    assertThat(fileHeader.getFileName()).isEqualTo(FILE_NAME_IN_ZIP);
    assertThat(fileHeader.getFileNameLength()).isEqualTo(FILE_NAME_IN_ZIP.length());
    verifyGeneralPurposeBytes(fileHeader.getGeneralPurposeFlag(), zipParameters);
    assertThat(fileHeader.getDiskNumberStart()).isEqualTo(isSplitZip ? diskNumberStart : 0);
    verifyLastModifiedFileTime(fileHeader, zipParameters);
    verifyExternalFileAttributes(fileHeader);
    assertThat(fileHeader.isDirectory()).isEqualTo(false);

    if (zipParameters.isWriteExtendedLocalFileHeader()) {
      assertThat(fileHeader.getUncompressedSize()).isEqualTo(0);
    } else {
      assertThat(fileHeader.getUncompressedSize()).isEqualTo(zipParameters.getEntrySize());
    }
    verifyCrc(fileHeader);
    assertThat(fileHeader.isDataDescriptorExists()).isEqualTo(zipParameters.isWriteExtendedLocalFileHeader());
    assertThat(fileHeader.getAesExtraDataRecord() != null).isEqualTo(aesExtraDataRecordPresent);
    assertThat(fileHeader.getExtraFieldLength()).isEqualTo(aesExtraDataRecordPresent ? InternalZipConstants.AES_EXTRA_DATA_RECORD_SIZE : 0);
  }

  private void verifyLocalFileHeader(LocalFileHeader localFileHeader, long lastModifiedFileTime) {
    assertThat(localFileHeader).isNotNull();
    assertThat(localFileHeader.getSignature()).isEqualTo(HeaderSignature.LOCAL_FILE_HEADER);
    assertThat(localFileHeader.getVersionNeededToExtract()).isEqualTo(20);
    assertThat(localFileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.STORE);
    assertThat(localFileHeader.getLastModifiedTime()).isEqualTo(lastModifiedFileTime);
    assertThat(localFileHeader.getUncompressedSize()).isEqualTo(1000L);
    assertThat(localFileHeader.getFileName()).isEqualTo(FILE_NAME_IN_ZIP);
    assertThat(localFileHeader.getFileNameLength()).isEqualTo(FILE_NAME_IN_ZIP.length());
    assertThat(localFileHeader.isEncrypted()).isEqualTo(true);
    assertThat(localFileHeader.getEncryptionMethod()).isEqualTo(EncryptionMethod.AES);
    assertThat(localFileHeader.getCrc()).isEqualTo(1231231L);
    assertThat(localFileHeader.getCompressedSize()).isEqualTo(23523L);
    assertThat(localFileHeader.getGeneralPurposeFlag()).containsExactly(2, 28);
    assertThat(localFileHeader.isDataDescriptorExists()).isTrue();
    assertThat(localFileHeader.getExtraFieldLength()).isEqualTo(190);
  }

  private void verifyExternalFileAttributes(FileHeader fileHeader) {
    if (FileUtils.isUnix() || FileUtils.isMac()) {
      if (FileUtils.isZipEntryDirectory(fileHeader.getFileName())) {
        assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(FileUtils.DEFAULT_POSIX_FOLDER_ATTRIBUTES);
      } else {
        assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(FileUtils.DEFAULT_POSIX_FILE_ATTRIBUTES);
      }
    } else {
      assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(new byte[4]);
    }




  }

  private void verifyCompressionMethod(FileHeader fileHeader, ZipParameters zipParameters) {
    if (fileHeader.isEncrypted() && fileHeader.getEncryptionMethod().equals(EncryptionMethod.AES)) {
      assertThat(fileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.AES_INTERNAL_ONLY);
    } else {
      assertThat(fileHeader.getCompressionMethod()).isEqualTo(zipParameters.getCompressionMethod());
    }
  }

  private void verifyLastModifiedFileTime(FileHeader fileHeader, ZipParameters zipParameters) {
    if (zipParameters.getLastModifiedFileTime() > 0) {
      assertThat(fileHeader.getLastModifiedTime()).isEqualTo(epochToExtendedDosTime(
          zipParameters.getLastModifiedFileTime()));
    } else {
      assertThat(fileHeader.getLastModifiedTime()).isGreaterThan(0);
    }
  }

  private void verifyGeneralPurposeBytes(byte[] generalPurposeBytes, ZipParameters zipParameters) {
    assertThat(generalPurposeBytes).isNotNull();
    assertThat(generalPurposeBytes.length).isEqualTo(2);
    assertThat(isBitSet(generalPurposeBytes[0], 0)).isEqualTo(zipParameters.isEncryptFiles());
    if (zipParameters.getCompressionMethod() == CompressionMethod.DEFLATE) {
      verifyCompressionLevelGridForDeflate(zipParameters.getCompressionLevel(),
          generalPurposeBytes[0]);
    } else {
      assertThat(isBitSet(generalPurposeBytes[0], 1)).isFalse();
      assertThat(isBitSet(generalPurposeBytes[0], 2)).isFalse();
    }
    assertThat(isBitSet(generalPurposeBytes[0], 3)).isEqualTo(zipParameters.isWriteExtendedLocalFileHeader());
    assertThat(isBitSet(generalPurposeBytes[1], 3)).isTrue();
  }

  private void verifyCompressionLevelGridForDeflate(CompressionLevel compressionLevel,
                                                    byte firstByteOfGeneralPurposeBytes) {
    if (CompressionLevel.NORMAL.equals(compressionLevel)) {
      assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 1)).isFalse();
      assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 2)).isFalse();
    } else if (CompressionLevel.MAXIMUM.equals(compressionLevel)) {
      assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 1)).isTrue();
      assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 2)).isFalse();
    } else if (CompressionLevel.FAST.equals(compressionLevel)) {
      assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 1)).isFalse();
      assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 2)).isTrue();
    } else if (CompressionLevel.FASTEST.equals(compressionLevel)
        || CompressionLevel.ULTRA.equals(compressionLevel)) {
      assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 1)).isTrue();
      assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 2)).isTrue();
    } else {
      throw new RuntimeException("Invalid compression level");
    }
  }

  private void verifyCrc(FileHeader fileHeader) {
    if (fileHeader.isEncrypted() && fileHeader.getEncryptionMethod() == EncryptionMethod.ZIP_STANDARD) {
      assertThat(fileHeader.getCrc()).isEqualTo(ENTRY_CRC);
    } else {
      assertThat(fileHeader.getCrc()).isEqualTo(0);
    }
  }

  private void verifyAesExtraDataRecord(AESExtraDataRecord aesExtraDataRecord, AesKeyStrength aesKeyStrength,
                                        CompressionMethod compressionMethod, AesVersion aesVersion) {
    assertThat(aesExtraDataRecord).isNotNull();
    assertThat(aesExtraDataRecord.getSignature()).isEqualTo(HeaderSignature.AES_EXTRA_DATA_RECORD);
    assertThat(aesExtraDataRecord.getDataSize()).isEqualTo(7);
    assertThat(aesExtraDataRecord.getVendorID()).isEqualTo("AE");
    assertThat(aesExtraDataRecord.getCompressionMethod()).isEqualTo(compressionMethod);
    assertThat(aesExtraDataRecord.getAesVersion()).isEqualTo(aesVersion);
    assertThat(aesExtraDataRecord.getAesKeyStrength()).isEqualTo(aesKeyStrength);
  }

  private void changeOsSystemPropertyToWindows() {
    System.setProperty("os.name", "windows");
  }

  private void changeOsSystemPropertyToUnix() {
    System.setProperty("os.name", "nux");
  }

  private void changeOsSystemPropertyToMac() {
    System.setProperty("os.name", "mac");
  }

  private void restoreOsSystemProperty() {
    System.setProperty("os.name", ACTUAL_OS);
  }
}