/*
 * Copyright 2017-present Facebook, 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 com.facebook.buck.tools.consistency;

import com.facebook.buck.log.thrift.ThriftRuleKeyLogger;
import com.facebook.buck.log.thrift.rulekeys.FullRuleKey;
import com.facebook.buck.testutil.integration.TemporaryPaths;
import com.facebook.buck.tools.consistency.RuleKeyFileParser.ParseException;
import com.facebook.buck.tools.consistency.RuleKeyFileParser.ParsedFile;
import com.google.common.collect.ImmutableMap;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

public class RuleKeyFileParserTest {

  @Rule public TemporaryPaths temporaryFolder = new TemporaryPaths();

  private String logPath;

  @Before
  public void setUp() throws InterruptedException, IOException {
    logPath = temporaryFolder.newFile("out.bin.log").toAbsolutePath().toString();
  }

  @Test
  public void parsesFileProperly() throws FileNotFoundException, ParseException {
    FullRuleKey ruleKey1 = new FullRuleKey("key1", "//:name1", "DEFAULT", ImmutableMap.of());
    FullRuleKey ruleKey2 = new FullRuleKey("key2", "//:name1", "other_type", ImmutableMap.of());
    FullRuleKey ruleKey3 = new FullRuleKey("key3", "//:name2", "OTHER", ImmutableMap.of());
    try (ThriftRuleKeyLogger logger = ThriftRuleKeyLogger.create(logPath)) {
      logger.write(ruleKey1);
      logger.write(ruleKey2);
      logger.write(ruleKey3);
    }

    RuleKeyFileParser parser = new RuleKeyFileParser();
    ParsedFile parsedFile = parser.parseFile(logPath, "//:name1");

    Assert.assertEquals("key1", parsedFile.rootHash);
    Assert.assertEquals(logPath, parsedFile.filename);
    Assert.assertTrue(parsedFile.parseTime.toNanos() > 0);
    Assert.assertEquals(3, parsedFile.rules.size());
    Assert.assertEquals(ruleKey1, parsedFile.rules.get("key1").ruleKey);
    Assert.assertEquals(ruleKey2, parsedFile.rules.get("key2").ruleKey);
    Assert.assertEquals(ruleKey3, parsedFile.rules.get("key3").ruleKey);
  }

  @Test(expected = ParseException.class)
  public void throwsIfTargetNotFound() throws FileNotFoundException, ParseException {
    FullRuleKey ruleKey1 = new FullRuleKey("key1", "//:name1", "DEFAULT", ImmutableMap.of());
    try (ThriftRuleKeyLogger logger = ThriftRuleKeyLogger.create(logPath)) {
      logger.write(ruleKey1);
    }

    RuleKeyFileParser parser = new RuleKeyFileParser();
    parser.parseFile(logPath, "//:invalid_name");
  }

  @Test(expected = ParseException.class)
  public void throwsOnBadLength() throws ParseException, IOException {
    FullRuleKey ruleKey1 = new FullRuleKey("key1", "//:name1", "DEFAULT", ImmutableMap.of());
    try (ThriftRuleKeyLogger logger = ThriftRuleKeyLogger.create(logPath)) {
      logger.write(ruleKey1);
    }
    try (DataOutputStream outputStream =
        new DataOutputStream(new FileOutputStream(logPath, true))) {
      outputStream.writeInt(1234);
      outputStream.flush();
    }

    RuleKeyFileParser parser = new RuleKeyFileParser();
    parser.parseFile(logPath, "//:name1");
  }

  @Test(expected = ParseException.class)
  public void throwsOnDeserializationError() throws ParseException, IOException {
    FullRuleKey ruleKey1 = new FullRuleKey("key1", "//:name1", "DEFAULT", ImmutableMap.of());
    try (ThriftRuleKeyLogger logger = ThriftRuleKeyLogger.create(logPath)) {
      logger.write(ruleKey1);
    }
    try (DataOutputStream outputStream =
        new DataOutputStream(new FileOutputStream(logPath, true))) {
      outputStream.writeInt(8);
      outputStream.writeInt(1234);
      outputStream.writeInt(2345);
      outputStream.flush();
    }

    RuleKeyFileParser parser = new RuleKeyFileParser();
    parser.parseFile(logPath, "//:name1");
  }
}

@@ -1247,16 +1247,58 @@
 it.Test;
+%0Aimport org.junit.rules.ExpectedException;
 %0A%0Apublic
@@ -1400,16 +1400,96 @@
 ths();%0A%0A
+  @Rule public ExpectedException expectedException = ExpectedException.none();%0A%0A
   privat
@@ -2796,41 +2796,8 @@
 Test
-(expected = ParseException.class)
 %0A  p
@@ -2871,32 +2871,160 @@
 arseException %7B%0A
+    expectedException.expectMessage(%22Could not find //:invalid_name in %22);%0A    expectedException.expect(ParseException.class);%0A%0A
     FullRuleKey 
@@ -3337,41 +3337,8 @@
 Test
-(expected = ParseException.class)
 %0A  p
@@ -3397,32 +3397,183 @@
 , IOException %7B%0A
+    expectedException.expectMessage(%22Invalid length specified. Expected 1234 bytes, only got 4%22);%0A    expectedException.expect(ParseException.class);%0A%0A
     FullRuleKey 
@@ -3900,32 +3900,88 @@
 writeInt(1234);%0A
+      outputStream.writeInt(0); // Add an extra 4 bytes%0A
       outputStre
@@ -4114,41 +4114,8 @@
 Test
-(expected = ParseException.class)
 %0A  p
@@ -4185,32 +4185,163 @@
 , IOException %7B%0A
+    expectedException.expectMessage(%22Could not deserialize array of size 8%22);%0A    expectedException.expect(ParseException.class);%0A%0A
     FullRuleKey 

Update exception verification in new test