#!/bin/bash
cvmfs_test_name="Change a Chunked File"
cvmfs_test_autofs_on_startup=false
cvmfs_test_suites="quick"

inflate_file() {
  local destination_file=$1
  local source_file=$2
  local desired_file_size=$3

  touch $destination_file
  while [ $(stat -c %s $destination_file) -lt $desired_file_size ]; do
    cat $source_file >> $destination_file
  done
}

produce_files_in() {
  local working_dir=$1

  pushdir $working_dir

  # create some small files that most likely get not chunked
  mkdir small_files
  echo "Die Sonne tönt nach alter Weise"     > small_files/verse1
  echo "In Brudersphären Wettgesang,"        > small_files/verse2
  echo "Und ihre vorgeschriebne Reise"       > small_files/verse3
  echo "Vollendet sie mit Donnergang."       > small_files/verse4
  echo "Ihr Anblick gibt den Engeln Stärke," > small_files/verse5
  echo "Wenn keiner sie ergründen mag;"      > small_files/verse6
  echo "Die unbegreiflich hohen Werke"       > small_files/verse7
  echo "Sind herrlich wie am ersten Tag."    > small_files/verse8

  # create a full poem in one file that will get concatinated later
  local bigtxtfile=small_files/heidenroeslein
  touch $bigtxtfile
  echo "Heidenröslein"                     >> $bigtxtfile
  echo ""                                  >> $bigtxtfile
  echo "Sah ein Knab' ein Röslein stehn, " >> $bigtxtfile
  echo "Röslein auf der Heiden, "          >> $bigtxtfile
  echo "War so jung und morgenschön, "     >> $bigtxtfile
  echo "Lief er schnell es nah zu sehn, "  >> $bigtxtfile
  echo "Sah's mit vielen Freuden. "        >> $bigtxtfile
  echo "Röslein, Röslein, Röslein rot, "   >> $bigtxtfile
  echo "Röslein auf der Heiden. "          >> $bigtxtfile
  echo ""                                  >> $bigtxtfile
  echo "Knabe sprach: ich breche diche, "  >> $bigtxtfile
  echo "Röslein auf der Heiden! "          >> $bigtxtfile
  echo "Röslein sprach: ich steche dich, " >> $bigtxtfile
  echo "Daß du ewig denkst an mich, "      >> $bigtxtfile
  echo "Und ich will's nicht leiden. "     >> $bigtxtfile
  echo "Röslein, Röslein, Röslein rot, "   >> $bigtxtfile
  echo "Röslein auf der Heiden. "          >> $bigtxtfile
  echo ""                                  >> $bigtxtfile
  echo "Und der wilde Knabe brach"         >> $bigtxtfile
  echo "'s Röslein auf der Heiden; "       >> $bigtxtfile
  echo "Röslein wehrte sich und stach, "   >> $bigtxtfile
  echo "Half ihr doch kein Weh und Ach, "  >> $bigtxtfile
  echo "Mußt' es eben leiden. "            >> $bigtxtfile
  echo "Röslein, Röslein, Röslein rot, "   >> $bigtxtfile
  echo "Röslein auf der Heiden."           >> $bigtxtfile
  echo ""                                  >> $bigtxtfile
  echo "  Johann Wolfgang von Goethe"      >> $bigtxtfile
  echo ""                                  >> $bigtxtfile
  echo ""                                  >> $bigtxtfile

  # create a big binary file that will get chunked
  mkdir big_file
  inflate_file big_file/1megabyte /bin/ls 1000000
  inflate_file big_file/10megabyte big_file/1megabyte 1000000
  inflate_file big_file/50megabyte big_file/10megabyte 50000000

  # create a big ascii text file that will get chunked
  inflate_file big_file/einige_heidenroeslein $bigtxtfile 100000
  inflate_file big_file/ein_paar_heidenroeslein big_file/einige_heidenroeslein 1000000
  inflate_file big_file/ein_paar_mehr_heidenroeslein big_file/ein_paar_heidenroeslein 10000000
  inflate_file big_file/viele_heidenroeslein big_file/ein_paar_mehr_heidenroeslein 60000000

  popdir
}

change_files_in() {
  local working_dir=$1

  pushdir $working_dir

  # remove the original 50 megabyte file and replace it by a
  # slightly different version
  local bigfile=big_file/50megabyte
  rm $bigfile
  touch $bigfile
  local first_hit=0
  while [ $(stat -c %s $bigfile) -lt 52428800 ]; do
    cat /bin/ls >> $bigfile
    if [ $first_hit -eq 0 ]; then
      echo "You need to understand recursion in order to understand recursion" >> $bigfile
      first_hit=1
    fi
  done

  popdir
}

check_chunk_alignment() {
  local repo_name=$1
  local old_clg_hash=$2
  local new_clg_hash=$3

  local old_catalog="$(download_and_decompress_object $repo_name ${old_clg_hash}C)"
  local new_catalog="$(download_and_decompress_object $repo_name ${new_clg_hash}C)"

  load_repo_config $repo_name

  local big_file="50megabyte" # see change_files_in()

  # compare the chunk sizes of associated chunks in the old and the new version
  # of the updated file.
  # the final output is the chunk difference which occured most frequently
  # --> in the best case this is 0 :-)
  most_common_size_difference=$(echo " \
  ATTACH '${old_catalog}' AS old; \
  ATTACH '${new_catalog}' AS new; \
  CREATE TEMPORARY TABLE old_offsets (rownum INTEGER PRIMARY KEY AUTOINCREMENT, offset INTEGER, size INTEGER); \
  CREATE TEMPORARY TABLE new_offsets (rownum INTEGER PRIMARY KEY AUTOINCREMENT, offset INTEGER, size INTEGER); \
  INSERT INTO old_offsets (offset, size) SELECT offset, size FROM old.chunks WHERE md5path_1 = (SELECT md5path_1 FROM old.catalog WHERE name = '${big_file}') ORDER BY offset; \
  INSERT INTO new_offsets (offset, size) SELECT offset, size FROM new.chunks WHERE md5path_1 = (SELECT md5path_1 FROM new.catalog WHERE name = '${big_file}') ORDER BY offset; \
  SELECT new.size - old.size FROM old_offsets AS old, new_offsets AS new WHERE old.rownum = new.rownum GROUP BY new.size - old.size ORDER BY count(new.size - old.size) DESC LIMIT 1;" | sqlite3)

  [ $most_common_size_difference -eq 0 ]
}

cvmfs_run_test() {
  logfile=$1
  local repo_dir=/cvmfs/$CVMFS_TEST_REPO

  local scratch_dir=$(pwd)
  mkdir reference_dir
  local reference_dir=$scratch_dir/reference_dir

  echo "check for sqlite3 utility"
  which sqlite3 || return 1

  echo "create a fresh repository named $CVMFS_TEST_REPO with user $CVMFS_TEST_USER"
  create_empty_repo $CVMFS_TEST_REPO $CVMFS_TEST_USER || return $?

  echo "starting transaction to edit repository"
  start_transaction $CVMFS_TEST_REPO || return $?

  echo "putting some stuff in the new repository"
  produce_files_in $repo_dir || return 3

  echo "putting exactly the same stuff in the scratch space for comparison"
  produce_files_in $reference_dir || return 4

  echo "creating CVMFS snapshot"
  publish_repo $CVMFS_TEST_REPO || return $?

  echo "compare the results of cvmfs to our reference copy"
  compare_directories $repo_dir $reference_dir || return $?

  echo "check catalog and data integrity"
  check_repository $CVMFS_TEST_REPO -i || return $?

  echo -n "retrieving name of root catalog for later comparison... "
  load_repo_config $CVMFS_TEST_REPO
  local old_root_catalog="$(get_current_root_catalog $CVMFS_TEST_REPO)"
  echo $old_root_catalog

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "starting transaction to edit repository"
  start_transaction $CVMFS_TEST_REPO || return $?

  echo "change the chunked file slightly"
  change_files_in $repo_dir || return 5

  echo "change the chunked file slightly in the reference directory"
  change_files_in $reference_dir || return 6

  echo "creating CVMFS snapshot"
  publish_repo $CVMFS_TEST_REPO || return $?

  echo "compare the results of cvmfs to our reference copy"
  compare_directories $repo_dir $reference_dir || return $?

  echo "check catalog and data integrity"
  check_repository $CVMFS_TEST_REPO -i || return $?

  echo -n "retrieving name of new root catalog... "
  local new_root_catalog="$(get_current_root_catalog $CVMFS_TEST_REPO)"
  echo $new_root_catalog

  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  echo "check if most chunks are still aligned after the edit"
  check_chunk_alignment $CVMFS_TEST_REPO $old_root_catalog $new_root_catalog || return 7

  return 0
}
