/*
 * 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.
 */


#include "net/instaweb/rewriter/public/resource_namer.h"

#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/md5_hasher.h"
#include "pagespeed/kernel/base/mock_hasher.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"

namespace net_instaweb {

namespace {

class ResourceNamerTest : public testing::Test {
 protected:
  ResourceNamerTest() { }

  void TestCopyAndSize(int signature_length) {
    MockHasher mock_hasher(full_name_.hash());
    ResourceNamer copy;
    copy.CopyFrom(full_name_);
    EXPECT_STREQ(copy.Encode(), full_name_.Encode());
    EXPECT_EQ(
        full_name_.EventualSize(mock_hasher, signature_length),
        full_name_.Encode().size());
  }

  ResourceNamer full_name_;
};

TEST_F(ResourceNamerTest, TestEncode) {
  // Stand up a minimal resource manager that only has the
  // resources we should need to encode.
  full_name_.set_id("id");
  full_name_.set_name("name.ext.as.many.as.I.like");
  full_name_.set_hash("hash");
  full_name_.set_ext("ext");
  TestCopyAndSize(0);
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.id.hash.ext",
               full_name_.Encode());
  EXPECT_STREQ("id.name.ext.as.many.as.I.like",
               full_name_.EncodeIdName());
  full_name_.set_experiment("q");
  TestCopyAndSize(0);
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.q.id.hash.ext",
               full_name_.Encode());
  full_name_.set_options("a=b");
  full_name_.set_experiment("");
  TestCopyAndSize(0);
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.a=b.id.hash.ext",
               full_name_.Encode());
  full_name_.set_name("name.ext");
  full_name_.set_options("options.with.dots");
  TestCopyAndSize(0);
  EXPECT_STREQ("name.ext.pagespeed.options.with.dots.id.hash.ext",
               full_name_.Encode());
  full_name_.set_options("options/with/slashes");
  EXPECT_STREQ("name.ext.pagespeed.options,_with,_slashes.id.hash.ext",
               full_name_.Encode());
  TestCopyAndSize(0);
}

TEST_F(ResourceNamerTest, TestDecode) {
  EXPECT_TRUE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  EXPECT_FALSE(full_name_.has_experiment());
  EXPECT_STREQ("id", full_name_.id());
  EXPECT_STREQ("name.ext.as.many.as.I.like", full_name_.name());
  EXPECT_STREQ("hash", full_name_.hash());
  EXPECT_STREQ("ext", full_name_.ext());
  TestCopyAndSize(0);
}

TEST_F(ResourceNamerTest, TestDecodeExperiment) {
  EXPECT_TRUE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.d.id.hash.ext",
                        4 /*hash size*/, 0 /*signature_size*/));
  TestCopyAndSize(0);
  EXPECT_STREQ("d", full_name_.experiment());
  EXPECT_TRUE(full_name_.has_experiment());
  EXPECT_STREQ("id", full_name_.id());
  EXPECT_STREQ("name.ext.as.many.as.I.like", full_name_.name());
  EXPECT_STREQ("hash", full_name_.hash());
  EXPECT_STREQ("ext", full_name_.ext());
  EXPECT_TRUE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.de.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  TestCopyAndSize(0);
  EXPECT_STREQ("", full_name_.experiment());
  EXPECT_STREQ("de", full_name_.options());
  EXPECT_FALSE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed..id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  EXPECT_FALSE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.D.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  EXPECT_FALSE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.`.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  EXPECT_FALSE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.{.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  EXPECT_TRUE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.a.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  TestCopyAndSize(0);
  EXPECT_TRUE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.z.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  TestCopyAndSize(0);
  EXPECT_TRUE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.z.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  TestCopyAndSize(0);
  EXPECT_STREQ("", full_name_.options());
  EXPECT_TRUE(full_name_.Decode("name.ext.pagespeed.opts.id.hash.ext",
                                4 /*hash size*/, 0 /*signature size*/));
  TestCopyAndSize(0);
  EXPECT_STREQ("opts", full_name_.options());

  // Make sure a decode without options clears them.
  EXPECT_TRUE(
      full_name_.Decode("name.ext.as.many.as.I.like.pagespeed.z.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  TestCopyAndSize(0);
  EXPECT_STREQ("", full_name_.options());
}

TEST_F(ResourceNamerTest, TestDecodeOptions) {
  EXPECT_TRUE(full_name_.Decode("name.ext.pagespeed.options.id.hash.ext",
                                4 /*hash size*/, 0 /*signature size*/));
  TestCopyAndSize(0);
  EXPECT_STREQ("id", full_name_.id());
  EXPECT_STREQ("options", full_name_.options());
  EXPECT_TRUE(
      full_name_.Decode("name.ext.pagespeed.options,_with,_slashes.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  TestCopyAndSize(0);

  EXPECT_STREQ("options/with/slashes", full_name_.options());
  EXPECT_TRUE(
      full_name_.Decode("name.ext.pagespeed.options,_with,_slashes.id.hash.ext",
                        4 /*hash size*/, 0 /*signature size*/));
  EXPECT_STREQ("options/with/slashes", full_name_.options());
  TestCopyAndSize(0);

  EXPECT_TRUE(full_name_.Decode(
      "name.ext.pagespeed.options,qwith,aquery=params.id.hash.ext",
      4 /*hash size*/, 0 /*signature size*/));
  EXPECT_STREQ("options?with&query=params", full_name_.options());
  TestCopyAndSize(0);
}

TEST_F(ResourceNamerTest, TestDecodeTooMany) {
  EXPECT_TRUE(full_name_.Decode("name.extra_dot.pagespeed.id.hash.ext",
                                4 /*hash size*/, 0 /*signature size*/));
}

TEST_F(ResourceNamerTest, TestDecodeNotEnough) {
  EXPECT_FALSE(
      full_name_.Decode("id.name.hash", 4 /*hash size*/, 0 /*signature size*/));
}

TEST_F(ResourceNamerTest, TestLegacyDecode) {
  // Legacydecode doesn't include signature at all, and will not use the same
  // decode function. Putting -1, -1 because that piece of the code should not
  // be hit.
  EXPECT_TRUE(full_name_.Decode("id.0123456789abcdef0123456789ABCDEF.name.js",
                                -1 /*hash size*/, -1 /*signature size*/));
  EXPECT_STREQ("id", full_name_.id());
  EXPECT_STREQ("name", full_name_.name());
  EXPECT_STREQ("0123456789abcdef0123456789ABCDEF", full_name_.hash());
  EXPECT_STREQ("js", full_name_.ext());
}

TEST_F(ResourceNamerTest, TestEventualSize) {
  MockHasher mock_hasher;
  GoogleString file = "some_name.pagespeed.idn.0.extension";
  EXPECT_TRUE(full_name_.Decode(file, 1 /*hash size*/, 0 /*signature size*/));
  EXPECT_EQ(file.size(),
            full_name_.EventualSize(mock_hasher, 0 /*signature size*/));
}

TEST_F(ResourceNamerTest, TestEventualSizeExperiment) {
  MockHasher mock_hasher;
  GoogleString file = "some_name.pagespeed.c.idn.0.extension";
  EXPECT_TRUE(full_name_.Decode(file, 1 /*hash size*/, 0 /*signature size*/));
  EXPECT_EQ(file.size(),
            full_name_.EventualSize(mock_hasher, 0 /*signature size*/));
}

TEST_F(ResourceNamerTest, TestEventualSizeSignature) {
  MockHasher mock_hasher;
  GoogleString file = "some_name.pagespeed.idn.0sig.extension";
  EXPECT_TRUE(full_name_.Decode(file, 1 /*hash size*/, 3 /*signature size*/));
  EXPECT_EQ(file.size(),
            full_name_.EventualSize(mock_hasher, 3 /*signature size*/));
  EXPECT_STREQ("sig", full_name_.signature());
  EXPECT_STREQ("extension", full_name_.ext());
  EXPECT_STREQ("0", full_name_.hash());
}

TEST_F(ResourceNamerTest, TestAllSignaturePossibilites) {
  // Test with the signature on.
  full_name_.set_id("id");
  full_name_.set_name("name.ext.as.many.as.I.like");
  full_name_.set_hash("hash");
  full_name_.set_ext("ext");
  full_name_.set_signature("sig");
  TestCopyAndSize(3);
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.id.hashsig.ext",
               full_name_.Encode());
  // Test with the signature off.
  full_name_.set_signature("");
  TestCopyAndSize(0);
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.id.hash.ext",
               full_name_.Encode());
  // Test with experiment on and signature on.
  full_name_.set_signature("sig");
  full_name_.set_experiment("q");
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.q.id.hashsig.ext",
               full_name_.Encode());
  // Test with experiment on and signature off.
  full_name_.set_signature("");
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.q.id.hash.ext",
               full_name_.Encode());
  // Test with experiment and options on and signature on can't be done.
  // Test with experiment and options on and signature off can't be done.
  // Test with options on and signature on.
  full_name_.set_experiment("");
  full_name_.set_options("a=b");
  full_name_.set_signature("sig");
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.a=b.id.hashsig.ext",
               full_name_.Encode());
  // Test with options on and signature off.
  full_name_.set_signature("");
  EXPECT_STREQ("name.ext.as.many.as.I.like.pagespeed.a=b.id.hash.ext",
               full_name_.Encode());
}

TEST_F(ResourceNamerTest, TestSizeWithoutHash_HashNotSet) {
  MD5Hasher md5_hasher;
  full_name_.set_name("file.css");
  full_name_.set_id("id");
  full_name_.set_ext("ext");
  EXPECT_EQ(STATIC_STRLEN("file.css") + STATIC_STRLEN("id") +
            STATIC_STRLEN("ext") + ResourceNamer::kOverhead +
            md5_hasher.HashSizeInChars(),
            full_name_.EventualSize(md5_hasher, 0  /*signature size*/));
}

}  // namespace

}  // namespace net_instaweb
