/*
 * Copyright 2010 Google 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.
 */

// Author: jmaessen@google.com (Jan Maessen)

#include "net/instaweb/util/public/data_url.h"

#include "net/instaweb/http/public/content_type.h"
#include "net/instaweb/util/public/basictypes.h"
#include "net/instaweb/util/public/string.h"
#include "net/instaweb/util/public/string_util.h"
#include "net/instaweb/util/public/gtest.h"

namespace net_instaweb {
namespace {

const GoogleString kAsciiData =
    "A_Rather=Long,But-conventional?looking_string#with;some:odd,characters.";
const GoogleString kAsciiDataBase64 =
    "QV9SYXRoZXI9TG9uZyxCdXQtY29udmVudGlvbmFsP2xvb2tpbmdfc3RyaW5nI3dpdGg7c29"
    "tZTpvZGQsY2hhcmFjdGVycy4=";

// A string with embedded NULs; we must construct it carefully to avoid
// truncation.
const char kMixedDataChars[] =
    "This string\ncontains\0lots of\tunusual\xe3~characters\xd7\xa5";
const char kMixedDataBase64[] =
    "VGhpcyBzdHJpbmcKY29udGFpbnMAbG90cyBvZgl1bnVzdWFs435jaGFyYWN0ZXJz16U=";

const char kPlainPrefix[] = "data:text/plain,";
const char kBase64Prefix[] = "data:text/plain;base64,";

const char kGifPlainPrefix[] = "data:image/gif,";
const char kGifBase64Prefix[] = "data:image/gif;base64,";

class DataUrlTest : public testing::Test {
 public:
  DataUrlTest()
      : mixed_data_(kMixedDataChars, STATIC_STRLEN(kMixedDataChars)) { }

 protected:
  // Make a ContentType yield readable failure output.  Needed this to fix bugs
  // in the tests!  (No actual program bugs found here...)
  const char* Mime(const ContentType* type) {
    if (type == NULL || type->mime_type() == NULL) {
      return "NULL";
    } else {
      return type->mime_type();
    }
  }

  void TestDecoding(const bool can_parse,
                    const bool can_decode,
                    const GoogleString& prefix,
                    const GoogleString& encoded,
                    const ContentType* type,
                    Encoding encoding,
                    const GoogleString& decoded) {
    GoogleString url = prefix + encoded;
    const ContentType* parsed_type;
    Encoding parsed_encoding = UNKNOWN;
    StringPiece parsed_encoded;
    EXPECT_EQ(can_parse,
              ParseDataUrl(url, &parsed_type,
                           &parsed_encoding, &parsed_encoded));
    EXPECT_EQ(encoding, parsed_encoding);
    EXPECT_EQ(type, parsed_type) << "type '" << Mime(type) <<
        "' didn't match '" << Mime(parsed_type) << "'\n";
    EXPECT_EQ(encoded, parsed_encoded);
    GoogleString parsed_decoded;
    EXPECT_EQ(can_decode,
              DecodeDataUrlContent(encoding, parsed_encoded, &parsed_decoded));
    EXPECT_EQ(decoded, parsed_decoded);
  }

  GoogleString mixed_data_;

 private:
  DISALLOW_COPY_AND_ASSIGN(DataUrlTest);
};

TEST_F(DataUrlTest, TestDataPlain) {
  GoogleString url;
  DataUrl(kContentTypeText, PLAIN, kAsciiData, &url);
  EXPECT_EQ(kPlainPrefix + kAsciiData, url);
}

TEST_F(DataUrlTest, TestDataBase64) {
  GoogleString url;
  DataUrl(kContentTypeText, BASE64, kAsciiData, &url);
  EXPECT_EQ(kBase64Prefix + kAsciiDataBase64, url);
}

TEST_F(DataUrlTest, TestData1Plain) {
  GoogleString url;
  DataUrl(kContentTypeGif, PLAIN, mixed_data_, &url);
  EXPECT_EQ(kGifPlainPrefix + mixed_data_, url);
}

TEST_F(DataUrlTest, TestData1Base64) {
  GoogleString url;
  DataUrl(kContentTypeGif, BASE64, mixed_data_, &url);
  EXPECT_EQ(StrCat(kGifBase64Prefix, kMixedDataBase64), url);
}

TEST_F(DataUrlTest, ParseDataPlain) {
  TestDecoding(true, true, kPlainPrefix, kAsciiData,
               &kContentTypeText, PLAIN, kAsciiData);
}

TEST_F(DataUrlTest, ParseDataBase64) {
  TestDecoding(true, true, kBase64Prefix, kAsciiDataBase64,
               &kContentTypeText, BASE64, kAsciiData);
}

TEST_F(DataUrlTest, ParseData1Plain) {
  TestDecoding(true, true, kPlainPrefix, mixed_data_,
               &kContentTypeText, PLAIN, mixed_data_);
}

TEST_F(DataUrlTest, ParseData1Base64) {
  TestDecoding(true, true, kBase64Prefix, kMixedDataBase64,
               &kContentTypeText, BASE64, mixed_data_);
}

TEST_F(DataUrlTest, ParseBadProtocol) {
  TestDecoding(false, false, "http://www.google.com/", "",
               NULL, UNKNOWN, "");
}

TEST_F(DataUrlTest, ParseNoComma) {
  TestDecoding(false, false,
               StrCat("data:text/plain;base64;", kMixedDataBase64), "",
               NULL, UNKNOWN, "");
}

TEST_F(DataUrlTest, ParseNoMime) {
  TestDecoding(true, true, "data:;base64,", kMixedDataBase64,
               NULL, BASE64, mixed_data_);
}

TEST_F(DataUrlTest, ParseCorruptMime) {
  TestDecoding(true, true, "data:#$!;base64,", kMixedDataBase64,
               NULL, BASE64, mixed_data_);
}

TEST_F(DataUrlTest, ParseBadEncodingIsPlain) {
  TestDecoding(true, true, "data:text/plain;mumbledypeg,", mixed_data_,
               &kContentTypeText, PLAIN, mixed_data_);
}

TEST_F(DataUrlTest, ParseBadBase64) {
  TestDecoding(true, false, kBase64Prefix, "@%#$%@#$%^@%%^%*%^&*",
               &kContentTypeText, BASE64, "");
}

}  // namespace
}  // namespace net_instaweb
