text:
Mr Abdul Basit and H.E. Naela Chohan ( Canberra ACT)
President of the Lahore Chamber of Commerce & Industry Mr Abdul Basit meets Pakistan High Commissioner for Australia Her Excellency Naela Chohan
Recently the President of the Lahore Chamber of Commerce & Industry Mr Abdul Basit and Pakistan High Commissioner for Australia Her Excellency Naela Chohan in a meeting at Canberra agreed to further cement trade and economic relations between Pakistan and Australia. There was also a consensus to adopt new methodology to expedite the volume of two-way trade.
High Commissioner Naela Chohan said that Pakistani Consulate in Sydney would share available business opportunities and would pave way for joint ventures between the private sectors of the two countries. She said that private sector would have to play an important role to enhance existing trade volume that does not match the potential of the two countries. HC Naela Chohan appreciated the LCCI efforts to promote external trade of Pakistan and promised to extend beset cooperation to promote bilateral trade.
The LCCI President Abdul Basit said that Pakistani Missions abroad should establish display centres for Pakistani products and cultural centres to promote Pakistani culture. These measures would help Pakistani products to get due share in the global market and would also attract foreigners towards Pakistan.
Abdul Basit said that the size of global market for Halal products is estimated dollar 3 trillion. Pakistan is a natural base for Halal products. If we could acquire 10% share in its trade then we can easily enhance our exports by more than two times. He said that Australia and Pakistan have been steady trading partners. With regard to major exporting countries of Pakistan, Australia takes 26th position. Similarly, Australia comes at 16th rank among the top importing countries to Pakistan.
"We are concerned about the comparatively low level of exports to Australia which results in unfavourable trade balance", the LCCI President added and said that the current level of exports to Australia does not match the actual export potential of Pakistan.
He said that our Embassies or High Commissions along with their subsidiary consulate offices have certain role to play to facilitate the business community of Pakistan who are very eager to develop trade relations with their counterparts in major economies. He said that any trade related information sharing with the chambers of commerce in Pakistan by the respective commercial sections of our embassies/high commissions can provide a lead to grab the possible business opportunities.
The LCCI President said that the commercial sections of other countries regularly share market surveys and commercial intelligence reports with their business communities which become basis to evaluate the chances of greater trade links. Pakistan will certainly welcome to have the same response from our own commercial sections on regular basis.
He said that trade diplomacy is another important tool that greatly helps in paving ways between two countries to increase the bilateral trade relations.
A brief report about the Lahore Chamber of Commerce and Industry delegation's visit to Australia
The Lahore Chamber of Commerce and Industry is the oldest and largest trade body in Pakistan with strong credentials having a large membership base standing around 20 thousands members. LCCI's history dates back to the early part of the last century. It has served the business community with great dexterity for almost 80 years consisting of four main phases including! Pre-1923 as part of Punjab-Haryana-Delhi chamber of commerce and industry; 1923-1947 as northern India chamber of commerce and industry; 1947-1960 as west Pakistan chamber of commerce and industry; and eventually as LCCI since 1960. We also take great pride in LCCI being the first iso certified chamber in Pakistan.
A 32-member executive committee headed by a president, senior vice president and vice president as office bearers manages this premier institution. The managing committee members are elected through annual election for a two-year term to work for the betterment of the business community on honorary basis.
Leers prime objective is to serve its members to their utmost satisfaction. It acts as a bridge between the government and the business community, and is regularly consulted by the government and international agencies on important national and international policy matters. In addition to its regular advisory and support services to the members, it actively promotes regional and international economic cooperation and trade.
The Lahore Chamber is involved in extensive research used in nearly every discipline, formulating recommendations and analysing various national and international policies affecting business, trade and industry.
It is committed to make effective contribution to the nation's economic development through the promotion of trade and industry. LCCI is also playing an important role to explore the potential markets for the promotion of Pakistan's exports through organization and participation in international trade fairs and exhibitions in CIS, UAE, FAREAST, USA, EUROPE AND AFRICA.
Mr. Abdul Basit- President, LCCI
It is an honor for me to present before you the report on LCCI's business delegation to Australia. It is worth mentioning that Australia is one of the great untapped potential markets. The total imports of Australia are over US$ 200 Billion while the total exports of Pakistan to Australia are just US$ 175 Million. Realizing the need to tap the tremendous potential in the Australian market, the LCCI arranged a business delegation to Australia.
The delegation proved to be extremely successful as various B2B meetings took place between the delegates and the members of Australian business community. These meetings provided an opportunity to the delegates to enhance trade linkages and explore new avenues for increasing trade volume.
I am Personally thankful to Pakistan's High Commissioner to Australia H.E Naela Chohan, and Pakistan Consul General in Sydney Mr. Majid Yousafani & Mr. Asadullah Jatoi Trade Development Officer for their unmatched cooperation and warm reception to LCCI delegation.
The meetings arranged by the High Commission and the consulate would go a long way to words bilateral economic ties.The sumptuous lunch arranged by the High Commission at Pakistan House would be remembered for years to come.
Oceania, continent between the Indian Ocean and the South Pacific Ocean
7,741,220 sq km
22,992,654 (July 2016 est.)
English 76.8%, Mandarin 1.6%, Italian 1.4%, Arabic 1.3%, Greek 1.2%, Cantonese 1.2%, Vietnamese 1.1%, other 10.4%, unspecified 5%
ETHNIC GROUPS:
English 25.9%, Australian 25.4%, Irish 7.5%, Scottish 6.4%, Italian 3.3%, German 3.2%, Chinese 3.1%, Indian 1.4%, Greek 1.4%, Dutch 1.2%, other 15.8% (includes Australian aboriginal .5%), unspecified 5.4%
NATURAL RESOURCES:
bauxite, coal, iron ore, copper, tin, gold, silver, uranium, nickel, tungsten, rare earth elements, mineral sands, lead, zinc, diamonds, natural gas, petroleum
GDP (Official Exchange Rate):
$1.257 trillion (2015 est.)
GDP -Real Growth Rate:
2.9% (2016 est.)
GDP - Per Capita (PPP):
$48,800 (2016 est.)
GDP - Composition, by Sector of Origin:
Agriculture: 3.6%
Inflation Rate (Consumer Prices):
Reserves of Foreign Exchange and Gold:
$54.3 billion (31 December 2016 est.)
Public Debt:
46.1% of GDP (2016 est.)
Taxes and Other Revenues:
Agriculture - Products:
wheat, barley, sugarcane, fruits; cattle, sheep, poultry
mining, industrial and transportation equipment, food processing, chemicals, steel
Exports:
$184.3 billion (2016 est.)
Exports - Commodities:
coal, iron ore, gold, meat, wool, alumina, wheat, machinery and transport equipment
Exports - Partners:
China 32.2%, Japan 15.9%, South Korea 7.1%, US 5.4%, India 4.2% (2015)
Imports - Commodities:
machinery and transport equipment, computers and office
machines, telecommunication equipment and parts; crude oil and petroleum products
Imports - Partners:
China 23%, US 11.2%, Japan 7.4%, South Korea 5.5%, Thailand 5.1%, Germany 4.6% (2015)
INVESTMENT OPPORTUNITIES IN AUSTRALIA
The Australian Government welcomes foreign investment. As the Australian Government's investment promotion agency, Austrade works to inform investors about growth opportunities in areas, jointly agreed by Australian and State and Territory Governments:
AGRIBUSINESS AND FOOD:
With global food demand booming, Australia is ideally placed to become a premium food supplier and a long-term partner of choice in food security. To help its agribusiness and food sector scale-up and make the most of growth opportunities, Australia is keen to attract investment in innovation, agriculture production incorporating food and fibre products, and advanced food processing.
MAJOR INFRASTRUCTURE:
Australia's buoyant economy, increased trade footprint, growing population and substantial pipeline of projects provide the ideal conditions to invest in major infrastructure. As an underpinning for economic growth, the Australian Government has identified infrastructure development as a priority and plays a leading role in building the country's assets, including road and rail networks. Significant opportunities exist for investors to finance, construct, own and operate Australia's transport, utilities and social infrastructure.
TOURISM INFRASTRUCTURE:
Australia's tourism industry has been identified as a growth sector due to its proximity to the fast growing economies in Asia, unique natural wonders and food experiences that are among the best in the world. As one of the world's most transparent real estate markets and with strong growth in visitor numbers and trip spend, Australia's tourism sector is attracting strong international investment interest.
RESOURCES AND ENERGY:
With abundant and diverse mineral and energy resources, a sophisticated equipment, technology and services industry, and proximity to growth markets in Asia, Australia offers a broad range of investment opportunities in the resources and energy sector.
DIGITAL TECHNOLOGIES:
Australia has an expanding, multi-faceted digital economy with e-ready government, industries and consumers and an accomplished and innovative ICT R&D sector.
The country's economic transition and the need for efficiency gains is driving demand for digital technologies and creating strong opportunities across economy, including in collaborative R&D, digital infrastructure and data centres, cyber security, financial services (fintech), agribusiness and food (agtech), resources and energy, digital health, transport and logistics, government services, education and tourism.
MEDICAL TECHNOLOGY:
A global leader in medical technologies, Australia offers investment opportunities ranging from discovery research through to product development partnerships in biotech, pharmaceuticals, medical devices, tropical medicines and digital health areas
.Investors can benefit from:
Research excellence and a collaborative culture
Accessible world-class research infrastructure
A track record of commercial success
An ideal market for testing new innovative medical products/technologies
Australia's trade, investment and cultural ties to the fast-growing Asia-Pacific region.
MATERIALS SCIENCE AND TECHNOLOGY:
Australia has advanced research and innovation capabilities in niche areas of advanced manufacturing and a proven track record in developing materials science and technology solutions, including in:
Composites research, including carbon fibres for aerospace, automotive and industrial
Light metals, such as titanium, processing including additive manufacturing
Bio-products for medical and industrial use, including through nanotechnology
Electronic and photonic materials, such as for batteries and energy storage
Australia offers world-class research infrastructure, a strong but flexible regulatory regime including (IP) protection, tax incentives and a supportive business culture for undertaking research and development (R&D).
PAKISTAN HIGH COMMISSION IN AUSTRALIA and CONSUL GENERAL IN SYDNEY PLAYING ACTIVE ROLE FOR BUSINESS PROMOTION
A seven member Business Delegation from Lahore Chamber of Commerce and Industry (LCCI) visited Australia from 14 to 20 February 2017 to attend meetings with the local business community and foreign trade-related institutions. Pakistan High Commission and Consulate General of Pakistan in Sydney played a vital role in bridging the gap between the Australian and Pakistani stakeholders by organizing their meetings. The delegation headed by Mr. Abdul Basit, President LCCI arrived in Sydney on 14-02-2017 to explore business opportunities in Australia with a focus on exports from Pakistan and to attract inward investment for Pakistan.
On 15th February 2017, the Delegation started their day at 9am by holding an introductory meeting with the Consul General of Pakistan in Sydney, Mr. Abdul Majid Yousfani that lasted for an hour. The Consul General briefed them on the trade potential that exists between Australia and Pakistan. LCCI President, MR. Abdul Basit, and other delegates agreed
that the existing volume of business between Pakistan and Austria has no match with their respective potentials.
After conclusion of the meeting, the delegation made its way to the NSW Parliament House where they met the Opposition whip, Hon. Shaoquett Moselmane, who gave a detailed briefing to the delegates about the rules and regulations of conducting business in Australia. The Honourable law maker also took the Business delegation to the Hall of the Lower House where proceedings were taking place. During the proceedings, the Chair duly acknowledged the visit of Pakistan's Business delegation to Australia.
Mr Abdul Basit, CG Abdul Majid Yousfani and Hon. Shaoquett Moselmane MP
Trade Delegation Members during the visit of New South Wales Parliament House
Visit to Victoria
A seven member Business Delegation from Lahore Chamber of Commerce and Industry (LCCI) visited Melbourne where they met community leaders and Business personalties.


You voted and here are the winners -- the Cellino & Barnes Best 8 Sabres players in Buffalo. Tweet your favorites using #TheBest8. So let us know on Twitter @Cellino_Barnes and keep checking in to find out the results.
You voted and here are the winners -- the Cellino & Barnes Best 8 Fall Beers in Buffalo. Tweet your favorites using #TheBest8. So let us know on Twitter @Cellino_Barnes and keep checking in to find out the results.
Tis the season! This time of year there is nothing we love more than fresh baked goods! Especially with the holidays approaching. So we asked you what your favorite Western New York bakeries were and you answered!
You voted and the results are in! What are the eight best items to have in your tailgate arsenal when you head to a Buffalo Bills game this season? What are those must have items that you can't do without?
With football season getting closer we get thinking about the good old days! So we asked you to name the best Bills players of all time! We asked and you answer! Here are your Cellino & Barnes Best 8 Buffalo Bills Players of All Time!
We asked and you responded. In an area of the world that's among the best for growing grapes, what are Buffalo's best wineries? The results are in. Here are your Cellino & Barnes Top 8 Buffalo wineries.
With football season getting closer what are the best places to take in a game? Any sport! We asked and you answer! Here are your Cellino & Barnes Top 8 Places to Watch Sports in Buffalo!
We asked and you voted! What are the best places for back-to-school shopping in Western New York?! Here are the Cellino & Barnes Best 8.


First impressions can make or break a sale, interview, or even potential relationship with a crush. The same goes for your business.
A website is there when you're not. Whether you focus on getting local clients or fully online, when you're sleeping, on vacation, or relaxing with friends, your site is the face of your company.
If the lead is fresh (meaning they just heard about you and clicked on a link to find out more), then it's even more important to make sure your site is leaving a good impression!
For example: two stores, selling the same exact thing, open next to each other. One invests a little more on the branded design – inside and out. The walls are painted, flowers placed outside, windows cleaned, and it's well-lit.
The other store does nothing. The business simply moves in.
Which would you more than likely enter? Which do you trust? The one that invested in their brand or the one that looks like they'll sell you something sketchy then disappear?
If your site is like the second store, it's ok! You just need to freshen things up a bit.
Your site doesn't exist anymore.
It's so embarrassing but years ago, I was hacked and didn't know it. I didn't regularly check my site and just kept working on freelance projects until someone mentioned that my site was… different. Thankfully, I had a backup.
Check the front end of your site every so often in case something's broken! Even if you weren't hacked, check plugins, links, forms, and po- ups to make sure it's all in working order.
You're getting people to your site but not converting!
This sometimes even happens with beautiful sites. It could be a few causes but from my experience it's either the layout of the site or the audience you're getting isn't into your branding. Your designer should understand your sales funnel and lay out a well-branded user experience that supports your goals.
What's a good tool to back this up? Google Analytics. Make sure it's installed and you're monitoring it in order to see the changes.
There's too much going on!
This happens with DIY entrepreneurs building their own site. They do many things- and are totally awesome at each one – so they want to put it all on the site.
Simplicity on a website is essential to better conversion.
Check your site and see if it feels cluttered. If it feels like there's 50 people in a shouting match for your attention, it's time to simplify. A designer could also edit the layout and make it work.
I can't properly see your site on my [insert mobile device].
Yup. As hard as it might be to believe, there's still sites that aren't even a tiny bit optimized for web.
According to research from places like Nielson Norman Group, Google, and a bunch more – mobile is better. Google even penalizes you if you're not thinking about how what user experience is like on small screens! It's just the way it is. So check your site on your phone, today.
Your site graphics were so trendy 5 years ago.
And now it's time for a change. Like a hairstyle, fashion, or your fad-diet, a site needs to change up every 1 to 3 years. But don't freak out!
The most painful updates are websites that have been around for 10 years. It often requires a complete overhaul. Others just need a few tweaks to colors, fonts, or photography.
After the initial update, get someone that will keep it updated. Depending on your business, this could be every month, 3 months, or 6 months.
Finally, if your business has taken a pivot, even slight, it might be time to redesign/refresh your website in order to meet your goals.


text:
Megyn Kelly Goes After The Media For Active Bias
Shelby Talcott Media Reporter
October 16, 2019 10:15 PM ET
Journalist Megyn Kelly went after the media Wednesday for what she described as blatant bias, noting that "it doesn't belong" and suggesting outlets now worry about being judged for not condemning President Donald Trump.
Kelly spoke to Fox News's Tucker Carlson on "Tucker Carlson Tonight" in her first television appearance in nearly a year and hit back at the alleged change in journalism over the years. Carlson showed a clip of CNN's Anderson Cooper giving a biased question to former Vice President Joe Biden, noting that scenes like this add "up to basically partisan interference in an election."
"You have journalists who feel that their first obligation is to help a candidate or hurt another candidate. That's not journalism," Carlson said, and Kelly agreed, adding she doesn't think subjective judgment should have a place in journalism.
"There was a premise that the other media, the mainstream media, maybe they weren't far left, but that there was a baked-in bias against people who believe in homeschooling, who believe in a pro-life position, who might have a gun," Kelly said, speaking about how the media has changed and describing how Fox News began. "And so they made a whole empire based on programming to those people. I think what's happened now on the other half of the country, on sort of the mainstream, is they have embraced it."
"It was sort of passive and now it's active. Now it's, 'Let's work. He's too bad. We have to work together. We'll be judged. History will judge as whether we were for or against this man [Trump].'" (RELATED: The Worst In Media Bias (So Far This Year)
Kelly noted this change came before voters elected Trump in 2016 and mentioned an interview she did with Jorge Ramos of Univision, who argued that journalists must become partisan and "abandon neutrality."
"He said, 'That's over. You've got to come out and you've got to say Trump's a racist. Trump's a misogynist. Trump's a bigot. Whatever your view is, you've got to say it and you've got to sell it, and that the time for neutrality has passed,'" she recalled. "And I do believe he's [Ramos] persuaded — and others have come to their own conclusion — that that's the way forward."
Tags : fox news megyn kelly tucker carlson
Shelby Talcott
Follow Shelby on Twitter

redpajama_set_name: RedPajamaCommonCrawl


A simple list of current hot coins/tokens voted by users. Each wallet can only vote once per 100 blocks. Smart Contract is deployed on mainnet.
How would you rate Hot Coins List?
Success! Thank you for rating Hot Coins List!


text:
Полянецкое () — село, относится к Савранскому району Одесской области Украины.

Население по переписи 2001 года составляло 1568 человек. Почтовый индекс — 66223. Телефонный код — 4865. Занимает площадь 4,87 км². Код КОАТУУ — 5124383201.

В селе родился, жил, умер и был похоронен Герой Советского Союза Терентий Мороз.

Ссылки
 Полянецкое на сайте Верховной рады Украины

 Полянецкое // История городов и сёл Украинской ССР

Населённые пункты Савранского района

redpajama_set_name: RedPajamaWikipedia


Yin and Yang Shop of Harmony. Catch the melody, the rhythm in the name.
Listen to the tinkle of the bell when you push open the door to a world so tiny it's like finding a precious jewel hidden in a little box wrapped in silk and velvet.
Here, sit down, have a sip of tea and a touch of sympathy. The store is full of charms – wondrous trinkets, medals and medallions for protection, for prosperity and every purpose under heaven. Indeed, the place is full of charm. Does enchantment work? Is there harmony between and among the elements that rule the cosmos? Are promises ever fulfilled?
Dear one, you hold the key.


MGM, 1933. Directed by Richard Boleslavsky. Camera: George Folsey. With Kay Francis, Nils Asther, Walter Huston, Phillips Holmes, Eugene Pallette, C. Henry Gordon, Louise Closser Hale, Jean Parker, Etienne Girardot, Mischa Auer, Leonid Kinsky.


First of all, a Revocable Living Trust generally does not provide asset protection. This upsets people at times because they really want protection and they have "heard" they can get it with a trust. We suspect people are confused because sometimes asset (i.e. creditor) protection is available with trusts, even with a revocable living trust.
Are you searching for a Greenwood, Indiana divorce and family law firm? Consider Family and Divorce Law in your search for quality representation. For Family Law, Divorce law and Custody Cases, there's no better firm to handle your case. If you're dealing with a legal issue regarding family law, make a call to the most trusted practice in the region at 317-886-6500.


The MS/ACCY program is offered through collaboration between the College of Business Administration and the College of Continuing Education. The degree is offered by the College of Business Administration and administered through the College of Continuing Education.
Minimum 2.5 overall undergraduate GPA.
Index #1 - (index = (GPA x 200) + GMAT score). An index of 1050 is required using the overall undergraduate GPA.
Index #2 - (index = (GPA x 200) + GMAT score). An index of 1100 is required using the last 60 semester units (last 90 quarter units) undergraduate GPA.
If the applicant has passed all four parts of the Certified Public Accountant examination but has not taken the GMAT, then a 500 GMAT score will be used to calculate admission indices #1 and #2. If the applicant has taken the LSAT but has not taken the GMAT, then the LSAT equivalent GMAT score will be used to calculate the admission indices #1 and #2. Actual GMAT scores take precedence over imputed GMAT scores as admission criteria for the CBA graduate business programs. LSAT minimum score of 150.
Licensed applicants must supply a copy of their CPA license.
If you attend Sacramento State, you must request an official transcript. We cannot obtain one on your behalf. You may order transcripts from the Office of the University Registrar at their Web site: csus.edu/gradstudies/forms/index. If you attended additional schools besides Sacramento State, you must obtain official transcripts from those institutions, this includes community and junior colleges.
$55 Application Fee (paid on-line when you file your application).
Please Note: Applications are not considered complete and will not be reviewed unless you complete all the steps as directed. You must submit official transcripts from ALL colleges and universities previously attended. Submission of official transcripts and test scores must be received no later than the application deadline.
International students will not be issued an F-1 student visa for this program as it is a completely online program. You may apply but you will not be issued a student visa to come to the United States. All materials, inclusive of TOEFL scores, must be turned into the Office of Graduate Studies. To ensure consideration, international students should submit their material to the International Admissions Office one month prior to the above CBA application deadlines.
The University requires a minimum TOEFL score of 550 (paper based), 80 (internet based), or 213 (computer based).
To understand the guidelines for your country please go to http://www.csus.edu/gradstudies/futurestudents/applicantswithforeigndocuments/index.html. Click on your country for detailed information.
California State University, Sacramento interprets "where English was the principal language of instruction" to mean that a school is located in a country where English is the native language (the daily medium of communication of the majority of the residents is English), and that the student received academic instruction in all subjects (except foreign language courses) at all levels of education in English.


With unique bottles and bold fragrances, Jean Paul Gaultier perfume for women are classic with intoxicating notes that leave a beautiful, lingering scent. The perfect day-to-night perfumes, Jean Paul Gaultier offers oriental-floral fragrances that work for any occasion.
Using notes like orange blossom, mandarin, rose, bergamot, plum, orchid and vanilla, Jean Paul Gaultier perfume creates exotic fragrances that are both sweet and intense. A dressing table favourite, these perfumes can be worn as your signature daytime scent, or saved for evening adventures.


The extreme youth and variety of the public (and guests) attending the International Animation Film Festival of Annecy (10–15 June 2013) is striking. This is because each edition, just as the 37th edition this year, enables a wide audience to discover the latest animated and mixed new techniques of the world's masterpieces, feature and short films, within and outside the competition; to get a taste of tomorrow's tendencies; to encounter their favorite authors, creators, animators, masters; and to discover future collaborators and partners. This year, a special focus pointed at Polish animation, with more than 50 films screened 'as a homage to the history and the vitality of Polish Cinema'.
Among the 500 plus films, there were some diploma films on show, as well as homages and retrospectives, special programs and previews, and Work in Progress.
Famous animators and newcomers, directors and producers, teachers and students, graphics designers and artists, aficionados, neophytes and the wide audience are mixed up in this very convivial festival. A huge plus, compared to other festivals across the world, is that the town hall of Annecy fully supports the event; hence the most picturesque venues are at the festival's disposal.
Annecy (France, International Animated Film Festival, June 10-15, 2013). Jury: Katharina Dockhorn, Germany ("Rheinpfalz"), Giulia Dobre, Romania ("Romanian National Radio"), Marcin Gizycki, Poland ("Kwartalnik Filmowy"). FIPRESCI Prize: Gloria Victoria by Theodore Ushev (Canada, 2013, 7 min). Print Source: ONF (NFB) – Office National Du Film Du Canada (National Film Board of Canada), Christina ROGERS. Address: P.O. Box 6100, Station Centre-ville, Montreal, Quebec, H3C 3H5, Canada. Web: www.nfb.ca, www.onf.ca. Special Mention: Feral by Daniel Sousa (USA, 2012, 13 min).
Autour du Lac, the Boundless Story. Giulia Dobre reviewed Around the Lake (Autour du Lac), which is a short animated film written and directed by Carl Roosens and Noémie Marsily, and presented in the Short Films Competition at Annecy 2013.
Between Art and Business. The International Animated Film Festival in Annecy, France, is the oldest and the most prestigious of all animation competitions in the world. The last time I had a pleasure to participate was in 1985. I can not even recall how and why I got there. Marcin Giżycki's impressions.
Canadian Animation at its Best. Canadian directors and animators are the winners of the Annecy International Animated Film Festival. Katharina Dockhorn's report.


On that new 2018 2250RK. Sales rep was awesome, manager was great. Finance guy, well, I don't have a lot of faith in him right now.
He called this morning, said email over pay stubs and come in tonight. So we did. He hadn't even read my email to submit the stubs. Now he's saying we should get all these other stips and bring them in tomorrow. Okay, why didn't he say that before? Wasted trip to Loveland and back in rush hour. He was rather flighty and couldn't make up his mind about what we did and didn't need to do. Then he pushes delivery to Saturday, cutting our vacation a day short.
My wife ain't happy. I ain't happy. He knows that now. He stepped out to chat with his manager, comes back and says Friday is back on.
Oh and he's out of the office tomorrow.
I'm home now and taking my aggressions out on cheap beer and there's a salmon filet on the smoker.


Our FAVORITE part of this business is hearing customer feedback, hands down. So reaching 100 customer reviews on Amazon in incredible, and we are so grateful. 94% of our ratings are 4 and star reviews. Yay! Thank you!!


text:
import sys,os
import tempfile, shutil
from io import BytesIO
import logging
import random
import time
import zlib

import unittest
try:
    from unittest import skip as _skip
except ImportError:
    # Python 2.6 has an older unittest API. The backported package is available from pypi.
    import unittest2 as unittest

# local modules
from utils import open_files

# Search parent directory first, to make sure we test the local nbt module,
# not an installed nbt module.
parentdir = os.path.realpath(os.path.join(os.path.dirname(__file__),os.pardir))
if parentdir not in sys.path:
    sys.path.insert(1, parentdir) # insert ../ just after ./

from nbt.region import RegionFile, RegionFileFormatError, NoRegionHeader, \
    RegionHeaderError, ChunkHeaderError, ChunkDataError, InconceivedChunk
from nbt.nbt import NBTFile, TAG_Compound, TAG_Byte_Array, TAG_Long, TAG_Int, TAG_String

REGIONTESTFILE = os.path.join(os.path.dirname(__file__), 'regiontest.mca')

### Helper Functions and Classes ###

def generate_level(bytesize = 4):
    """Generate a level, which is a given size in bytes."""
    level = NBTFile() # Blank NBT
    def append_byte_array(name, bytesize=1000):
        bytesize -= len(name)
        bytesize -= 7
        # byte_array = TAG_Byte_Array(name=name, value=bytearray([random.randrange(256) for i in range(bytesize)]))
        # level.append(byte_array)
        byte_array = TAG_Byte_Array(name=name)
        byte_array.value = bytearray([random.randrange(256) for i in range(bytesize)])
        level.tags.append(byte_array)
    random.seed(123456789) # fixed seed to give predictable results.
    if bytesize < 13:
        raise ValueError("NBT file size is at least 13 bytes")
    # 4 bytes TAG_Compound overhead, 13 bytes TAG_Byte_Array overhead.
    bytesize -= 4 # deduce overhead bytes
    i = 1
    while bytesize > 1500:
        append_byte_array("%06d" % i, 1000)
        i += 1
        bytesize -= 1000
    append_byte_array("last", bytesize)
    return level

def generate_compressed_level(minsize = 2000, maxsize = None):
    """
    Generate a level, which -when zlib compressed- is the given size in bytes.
    Note: this returns the *UNCOMPRESSED* NBT file.
    """
    logger = logging.getLogger("nbt.tests.regiontests")
    if maxsize == None:
        maxsize = minsize
    targetsize = (minsize + maxsize) // 2
    bytesize = targetsize
    c = None
    tries = 0
    while True:
        # Generate a level, encode to binary and compress
        level = generate_level(bytesize)
        b = BytesIO()
        level.write_file(buffer=b)
        b = b.getvalue()
        assert len(b) == bytesize
        c = zlib.compress(b, 1)
        # check if the compressed size is sufficient.
        resultsize = len(c)
        logger.debug("try %d: uncompressed %d bytes -> compressed %d bytes" % \
                     (tries, bytesize, resultsize))
        if minsize <= resultsize <= maxsize:
            break
        # size is not good enough. Try again, with new targetsize.
        bytesize = int(round(bytesize * targetsize / resultsize))
        tries += 1
        if tries > 20:
            sys.stderr.write(("Failed to generate NBT file of %d bytes after %d tries. " + \
                             "Result is %d bytes.\n") % (targetsize, tries, resultsize))
            break
    return level

class PedanticFileWrapper(object):
    """Pedantic wrapper around a file object.
    Is guaranteed to raise an IOError if an attempt is made to:
    - seek to a location larger than the file
    - read behind the file boundary
    Only works for random access files that support seek() and tell().
    """
    def __init__(self, stream):
        """Create a new wrapper. stream must be a file object."""
        self.__stream = stream
    def seek(self, offset, whence = 0):
        pos = self.__stream.tell()
        self.__stream.seek(0, 2) # end of file
        length = self.__stream.tell()
        if whence == 1:
            offset = pos + offset
        elif whence == 2:
            offset = length + offset
        result = self.__stream.seek(offset)
        if offset > length:
            raise IOError("Attempt to seek at offset %d for file of %d bytes" % (offset, length))
        elif offset < 0:
            raise IOError("Attempt to seek at offset %d for file of %d bytes" % (offset, length))
        return result
    def read(self, size = -1):
        pos = self.__stream.tell()
        self.__stream.seek(0, 2) # end of file
        length = self.__stream.tell()
        self.__stream.seek(pos)
        if pos + size > length:
            raise IOError("Attempt to read bytes %d to %d from file of %d bytes" % \
                        (pos, pos + size, length))
        return self.__stream.read(size)
    def __getattr__(self, name):
        return getattr(self.__stream, name)
    def __str__(self):
        return str(self.__stream)
    def __repr__(self):
        return str(self.__stream)

### Actual Test Classes ###

class ReadWriteTest(unittest.TestCase):
    """Test to read, write and relocate chunks in a region file."""

    """
    All tests operate on regiontest.mca, is a 27-sector large region file, which looks like:
    sector 000: locations
    sector 001: timestamps
    sector 002: chunk 6 ,0  part 1/1
    sector 003: chunk 7 ,0  part 1/1 <<-- minor warning: unused bytes not zeroed
    sector 004: empty                <<-- minor warning: bytes not zeroed
    sector 005: chunk 8 ,0  part 1/1
    sector 006: chunk 9 ,0  part 1/1
    sector 007: chunk 10,0  part 1/1 <<-- deprecated encoding (gzip = 1)
    sector 008: chunk 11,0  part 1/1 <<-- unknown encoding (3)
    sector 009: chunk 2 ,0  part 1/1 <<-- uncompressed (encoding 0)
    sector 010: empty
    sector 011: empty
    sector 012: chunk 3 ,0  part 1/1 <<-- garbled data (can't be decoded)
    sector 013: chunk 1 ,0  part 1/1
    sector 014: chunk 4 ,0  part 1/3 <<-- 1 sector required, but 3 sectors allocated
    sector 015: chunk 12,0  part 1/1 <<-- part 2 of chunk 4,0 overlaps
    sector 016: chunk 4, 0  part 3/3
    sector 017: chunk 16,0  part 1/2
    sector 018: chunk 16,0  part 2/2
    sector 019: chunk 5 ,1  part 1/2 <<-- correct encoding, but not a valid NBT file
    sector 020: chunk 5 ,1  part 2/2
    sector 021: chunk 6 ,1  part 1/1 <<-- potential overlap with empty chunk 13,0
    sector 022: chunk 7 ,1  part 1/1 <<-- no timestamp
    sector 023: chunk 4 ,1  part 1/1 <<-- zero-byte length value in chunk (invalid header)
    sector 024: chunk 8 ,1  part 1/1 <<-- one-byte length value in chunk (no data)
    sector 025: chunk 3 ,1  part 1/1 <<-- 2 sectors required, but 1 sector allocated (length 4+1+4092)
    sector 026: empty                <<-- unregistered overlap from chunk 3,1

    in addition, the following (corrupted) chunks are defined in the header of regiontest.mca:
    sector 021: 0-sector length chunk 13,0 (and overlapping with chunk 6,1)
    sector 001: chunk 14,0 (in header)
    sector 030: chunk 15,0 (out of file)
    ----------: chunk 17,0 timestamp without data

    Thus:
    01. chunk 1 ,0  Readable
    02. chunk 2 ,0  Readable   <<-- uncompressed (encoding 0)
    03. chunk 3 ,0  Unreadable <<-- garbled data (can't be decoded)
    04. chunk 4 ,0  Readable   <<-- overlaps with chunk 12,0.
    05. chunk 6 ,0  Readable
    06. chunk 7 ,0  Readable
    07. chunk 8 ,0  Readable
    08. chunk 9 ,0  Readable
    09. chunk 10,0  Readable   <<-- deprecated encoding (gzip = 1)
    10. chunk 11,0  Unreadable <<-- unknown encoding (3)
    11. chunk 12,0  Readable   <<-- Overlaps with chunk 4,0.
    12. chunk 13,0  Unreadable <<-- 0-sector length in header
    13. chunk 14,0  Unreadable <<-- in header
    14. chunk 15,0  Unreadable <<-- out of file
    15. chunk 16,0  Readable
    --  chunk 17,0  Unreadable <<-- timestamp without data
    16. chunk 3 ,1  Readable   <<-- 2 sectors required, but 1 sector allocated (length 4+1+4092)
    17. chunk 4 ,1  Unreadable <<-- zero-byte length value in chunk (invalid header)
    18. chunk 5 ,1  Readable   <<-- Not a valid NBT file
    19. chunk 6 ,1  Readable   <<-- potential overlap with empty chunk 13,0
    20. chunk 7 ,1  Readable   <<-- no timestamp
    21. chunk 8 ,1  Unreadable <<-- one-byte length value in chunk (no data)
    """

    def setUp(self):
        self.tempdir = tempfile.mkdtemp()
        self.filename = os.path.join(self.tempdir, 'regiontest.mca')
        shutil.copy(REGIONTESTFILE, self.filename)
        self.region = RegionFile(filename = self.filename)

    def tearDown(self):
        del self.region
        try:
            shutil.rmtree(self.tempdir)
        except OSError as e:
            raise

    def test000MethodFileSize(self):
        """
        Test of the get_size() method.
        The regionfile has 27 sectors.
        """
        self.assertEqual(self.region.get_size(), 27*4096)

    def test001MethodChunkCount(self):
        """
        Test of the chunk_count() method.
        The regionfile has 21 chunks, including 3-out of file chunks.
        """
        self.assertEqual(self.region.chunk_count(), 21)

    def test002MethodGetChunkCoords(self):
        """
        Test of get_chunk_coords() method.
        Note: this function may be deprecated in a later version of NBT.
        """
        coords_and_lengths = self.region.get_chunk_coords()
        coords = []
        for coord in coords_and_lengths:
            coords.append((coord['x'], coord['z']))

        self.assertIn((1, 0), coords)
        self.assertIn((2, 0), coords)
        self.assertIn((3, 0), coords)
        self.assertIn((4, 0), coords)
        self.assertIn((6, 0), coords)
        self.assertIn((7, 0), coords)
        self.assertIn((8, 0), coords)
        self.assertIn((9, 0), coords)
        self.assertIn((10, 0), coords)
        self.assertIn((11, 0), coords)
        self.assertIn((12, 0), coords)
        self.assertIn((13, 0), coords)
        self.assertIn((14, 0), coords) # note: length is undefined
        self.assertIn((15, 0), coords) # note: length is undefined
        self.assertIn((16, 0), coords)
        self.assertNotIn((17, 0), coords)
        self.assertIn((3, 1), coords)
        self.assertIn((4, 1), coords)
        self.assertIn((5, 1), coords)
        self.assertIn((6, 1), coords)
        self.assertIn((7, 1), coords)
        self.assertIn((8, 1), coords)
        self.assertEqual(len(coords_and_lengths), 21)

    def test003MethodIterChunks(self):
        """
        Test of iter_chunks() method.
        """
        chunks = []
        for chunk in self.region.iter_chunks():
            self.assertIsInstance(chunk, TAG_Compound)
            chunks.append(chunk)
        self.assertEqual(len(chunks), 13)

    def test004SyntaxIterChunks(self):
        """
        Test of iter(RegionFile) syntax.
        """
        chunks = []
        for chunk in self.region:
            self.assertIsInstance(chunk, TAG_Compound)
            chunks.append(chunk)
        self.assertEqual(len(chunks), 13)

    def test005ParameterHeaders(self):
        """
        read headers of chunk 9,0:
        sector 6, 1 sector length, timestamp 1334530101, status STATUS_CHUNK_OK.
        read chunk headers of chunk 9,0:
        length (incl. compression byte): 3969 bytes, zlip (2) compression, status STATUS_CHUNK_OK.
        """
        self.assertEqual(self.region.header[9,0], (6, 1, 1334530101, RegionFile.STATUS_CHUNK_OK))
        self.assertEqual(self.region.chunk_headers[9,0], (3969, 2, RegionFile.STATUS_CHUNK_OK))

    def test006ParameterHeadersUndefinedChunk(self):
        """
        read headers & chunk_headers of chunk 2,2
        """
        self.assertEqual(self.region.header[2,2], (0, 0, 0, RegionFile.STATUS_CHUNK_NOT_CREATED))
        self.assertEqual(self.region.chunk_headers[2,2], \
                        (None, None, RegionFile.STATUS_CHUNK_NOT_CREATED))

    def test010ReadChunkZlibCompression(self):
        """
        chunk 9,0: regular Zlib compression. Should be read OK.
        """
        nbt = self.region.get_nbt(9, 0)
        self.assertIsInstance(nbt, TAG_Compound)
        # get_chunk is currently an alias of get_nbt
        chunk = self.region.get_chunk(9, 0)
        self.assertIsInstance(chunk, TAG_Compound)

    def test011ReadChunkGzipCompression(self):
        """
        chunk 10,0: deprecated GZip compression. Should be read OK.
        """
        nbt = self.region.get_nbt(10, 0)
        self.assertIsInstance(nbt, TAG_Compound)

    def test012ReadChunkUncompressed(self):
        """
        chunk 2,0: no compression. Should be read OK.
        """
        nbt = self.region.get_nbt(2, 0)
        self.assertIsInstance(nbt, TAG_Compound)

    def test013ReadUnknownEncoding(self):
        """
        chunk 11,0 has unknown encoding (3). Reading should raise a ChunkDataError.
        """
        self.assertRaises(ChunkDataError, self.region.get_nbt, 11, 0)

    def test014ReadMalformedEncoding(self):
        """
        chunk 3,0 has malformed content. Reading should raise a ChunkDataError.
        This should not raise a MalformedFileError.
        """
        self.assertRaises(ChunkDataError, self.region.get_nbt, 3, 0)

    def test015ReadMalformedNBT(self):
        """
        read chunk 5,1: valid compression, but not a valid NBT file.
        Reading should raise a ChunkDataError, not a nbt.nbt.MalformedFileError.
        """
        self.assertRaises(ChunkDataError, self.region.get_nbt, 5, 1)

    def test016ReadChunkNonExistent(self):
        """
        read chunk 2,2: does not exist. Reading should raise a InconceivedChunk.
        """
        self.assertRaises(InconceivedChunk, self.region.get_nbt, 2, 2)

    def test017ReadableChunks(self):
        """
        Test which chunks are readable.
        """
        coords = []
        for cc in self.region.get_chunk_coords():
            try:
                nbt = self.region.get_chunk(cc['x'], cc['z'])
                coords.append((cc['x'], cc['z']))
            except RegionFileFormatError:
                pass

        self.assertIn((1, 0), coords)
        self.assertIn((2, 0), coords)
        self.assertNotIn((3, 0), coords) # garbled data
        self.assertIn((4, 0), coords) # readable, despite overlapping with chunk 12,0
        self.assertIn((6, 0), coords)
        self.assertIn((7, 0), coords)
        self.assertIn((8, 0), coords)
        self.assertIn((9, 0), coords)
        self.assertIn((10, 0), coords)
        self.assertNotIn((11, 0), coords) # unknown encoding
        self.assertIn((12, 0), coords) # readable, despite overlapping with chunk 4,1
        self.assertNotIn((13, 0), coords) # zero-length (in header)
        self.assertNotIn((14, 0), coords) # in header
        self.assertNotIn((15, 0), coords) # out of file
        self.assertIn((16, 0), coords)
        self.assertNotIn((17, 0), coords) # timestamp without data
        self.assertIn((3, 1), coords)
        self.assertNotIn((4, 1), coords) # invalid length (in chunk)
        self.assertNotIn((5, 1), coords) # not a valid NBT file
        self.assertIn((6, 1), coords)
        self.assertIn((7, 1), coords)
        self.assertNotIn((8, 1), coords) # zero-length (in chunk)
        self.assertEqual(len(coords), 13)

    def test020ReadInHeader(self):
        """
        read chunk 14,0: supposedly located in the header.
        Reading should raise a RegionHeaderError.
        """
        self.assertRaises(RegionHeaderError, self.region.get_nbt, 14, 0)
        self.assertEqual(self.region.header[14,0],
                         (1, 1, 1376433960, RegionFile.STATUS_CHUNK_IN_HEADER))
        self.assertEqual(self.region.chunk_headers[14,0],
                         (None, None, RegionFile.STATUS_CHUNK_IN_HEADER))

    def test021ReadOutOfFile(self):
        """
        read chunk 15,0: error (out of file)
        """
        self.assertRaises(RegionHeaderError, self.region.get_nbt, 15, 0)
        self.assertEqual(self.region.header[15,0],
                         (30, 1, 1376433961, RegionFile.STATUS_CHUNK_OUT_OF_FILE))
        self.assertEqual(self.region.chunk_headers[15,0],
                         (None, None, RegionFile.STATUS_CHUNK_OUT_OF_FILE))

    def test022ReadZeroLengthHeader(self):
        """
        read chunk 13,0: error (zero-length)
        """
        self.assertRaises(RegionHeaderError, self.region.get_nbt, 13, 0)
        self.assertEqual(self.region.header[13,0],
                         (21, 0, 1376433958, RegionFile.STATUS_CHUNK_ZERO_LENGTH))
        self.assertEqual(self.region.chunk_headers[13,0],
                         (None, None, RegionFile.STATUS_CHUNK_ZERO_LENGTH))

    def test023ReadInvalidLengthChunk(self):
        """
        zero-byte lengths in chunk. (4,1)
        read chunk 4,1: error (invalid)
        """
        self.assertRaises(ChunkHeaderError, self.region.get_nbt, 4, 1)

    def test024ReadZeroLengthChunk(self):
        """
        read chunk 8,1: error (zero-length chunk)
        """
        self.assertRaises(ChunkHeaderError, self.region.get_nbt, 8, 1)

    def test025ReadChunkSizeExceedsSectorSize(self):
        """
        read chunk 3,1: can be read, despite that the chunk content is longer
        than the allocated sectors.
        In general, reading should either succeeds, or raises a ChunkDataError.
        The status should be STATUS_CHUNK_MISMATCHED_LENGTHS.
        """
        self.assertEqual(self.region.chunk_headers[3,1][2],
                         RegionFile.STATUS_CHUNK_MISMATCHED_LENGTHS)
        # reading should succeed, despite the overlap (next chunk is free)
        nbt = self.region.get_nbt(3, 1)

    def test026ReadChunkOverlapping(self):
        """
        chunk 4,0 and chunk 12,0 overlap: status should be STATUS_CHUNK_OVERLAPPING
        """
        self.assertEqual(self.region.chunk_headers[4,0][2],
                         RegionFile.STATUS_CHUNK_OVERLAPPING)
        self.assertEqual(self.region.chunk_headers[12,0][2],
                         RegionFile.STATUS_CHUNK_OVERLAPPING)

    def test030GetTimestampOK(self):
        """
        get_timestamp
        read chunk 9,0: OK
        """
        self.assertEqual(self.region.get_timestamp(9,0), 1334530101)

    def test031GetTimestampBadChunk(self):
        """
        read chunk 15,0: OK
        Data is out-out-of-file, but timestamp is still there.
        """
        self.assertEqual(self.region.get_timestamp(15,0), 1376433961)

    def test032GetTimestampNoChunk(self):
        """
        read chunk 17,0: OK
        no data, but a timestamp
        """
        self.assertEqual(self.region.get_timestamp(17,0), 1334530101)

    def test033GetTimestampMissing(self):
        """
        read chunk 7,1: OK
        data, but no timestamp
        """
        self.assertEqual(self.region.get_timestamp(7,1), 0)

    def test040WriteNewChunk(self):
        """
        read chunk 0,2: InconceivedError
        write 1 sector chunk 0,2
        - read location (<= 026), size (001), timestamp (non-zero).
        """
        chunk_count = self.region.chunk_count()
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.assertRaises(InconceivedChunk, self.region.get_nbt, 0, 2)
        timebefore = int(time.time())
        self.region.write_chunk(0, 2, nbt)
        timeafter = time.time()
        header = self.region.header[0,2]
        self.assertEqual(header[1], 1, "Chunk length must be 1 sector")
        self.assertGreaterEqual(header[0], 2, "Chunk must not be written in the header")
        self.assertLessEqual(header[0], 26, "Chunk must not be written in an empty sector")
        self.assertGreaterEqual(header[2], timebefore, "Timestamp must be time.time()")
        self.assertLessEqual(header[2], timeafter, "Timestamp must be time.time()")
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_OK)
        self.assertEqual(self.region.chunk_count(), chunk_count + 1)

    def test041WriteAndReadNewChunk(self):
        """
        write 1 sector chunk 0,2
        read chunk 0,2: OK
        - compare writen and read NBT file
        """
        nbtwrite = generate_compressed_level(minsize = 100, maxsize = 4000)
        writebuffer = BytesIO()
        nbtwrite.write_file(buffer=writebuffer)
        nbtsize = writebuffer.seek(0,2)
        self.region.write_chunk(0, 2, nbtwrite)
        nbtread = self.region.get_nbt(0, 2)
        readbuffer = BytesIO()
        nbtread.write_file(buffer=readbuffer)
        self.assertEqual(nbtsize, readbuffer.seek(0,2))
        writebuffer.seek(0)
        writtendata = writebuffer.read()
        readbuffer.seek(0)
        readdata = readbuffer.read()
        self.assertEqual(writtendata, readdata)

    def test042WriteExistingChunk(self):
        """
        write 1 sector chunk 9,0 (should stay in 006)
        - read location (006) and size (001).
        """
        chunk_count = self.region.chunk_count()
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.region.write_chunk(9, 0, nbt)
        header = self.region.header[9, 0]
        self.assertEqual(header[0], 6, "Chunk should remain at sector 6")
        self.assertEqual(header[1], 1, "Chunk length must be 1 sector")
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_OK)
        self.assertEqual(self.region.chunk_count(), chunk_count)

    def test043DeleteChunk(self):
        """
        read chunk 6,0: OK
        unlink chunk 6,0
        - check location, size, timestamp (all should be 0)
        read chunk 6,0: InconceivedError
        """
        chunk_count = self.region.chunk_count()
        nbt = self.region.get_nbt(6, 0)
        self.region.unlink_chunk(6, 0)
        self.assertRaises(InconceivedChunk, self.region.get_nbt, 6, 0)
        header = self.region.header[6, 0]
        self.assertEqual(header[0], 0)
        self.assertEqual(header[1], 0)
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_NOT_CREATED)
        self.assertEqual(self.region.chunk_count(), chunk_count - 1)

    def test044UseEmptyChunks(self):
        """
        write 1 sector chunk 1,2 (should go to 004)
        write 1 sector chunk 2,2 (should go to 010)
        write 1 sector chunk 3,2 (should go to 011)
        verify file size remains 027*4096
        """
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        availablelocations = (4, 10, 11, 26)
        self.region.write_chunk(1, 2, nbt)
        self.assertIn(self.region.header[1, 2][0], availablelocations)
        self.region.write_chunk(2, 2, nbt)
        self.assertIn(self.region.header[2, 2][0], availablelocations)
        self.region.write_chunk(3, 2, nbt)
        self.assertIn(self.region.header[3, 2][0], availablelocations)

    def test050WriteNewChunk2sector(self):
        """
        write 2 sector chunk 1,2 (should go to 010-011)
        """
        nbt = generate_compressed_level(minsize = 5000, maxsize = 7000)
        self.region.write_chunk(1, 2, nbt)
        header = self.region.header[1, 2]
        self.assertEqual(header[1], 2, "Chunk length must be 2 sectors")
        self.assertEqual(header[0], 10, "Chunk should be placed in sector 10")
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_OK)

    def test051WriteNewChunk4096byte(self):
        """
        write 4091+5-byte (1 sector) chunk 1,2 (should go to 004)
        """
        nbt = generate_compressed_level(minsize = 4091, maxsize = 4091)
        self.region.write_chunk(1, 2, nbt)
        header = self.region.header[1, 2]
        chunk_header = self.region.chunk_headers[1, 2]
        if chunk_header[0] != 4092:
            raise unittest.SkipTest("Can't create chunk of 4091 bytes compressed")
        self.assertEqual(header[1], 1, "Chunk length must be 2 sectors")
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_OK)

    def test052WriteNewChunk4097byte(self):
        """
        write 4092+5-byte (2 sector) chunk 1,2 (should go to 010-011)
        """
        nbt = generate_compressed_level(minsize = 4092, maxsize = 4092)
        self.region.write_chunk(1, 2, nbt)
        header = self.region.header[1, 2]
        chunk_header = self.region.chunk_headers[1, 2]
        if chunk_header[0] != 4093:
            raise unittest.SkipTest("Can't create chunk of 4092 bytes compressed")
        self.assertEqual(header[1], 2, "Chunk length must be 2 sectors")
        self.assertEqual(header[0], 10, "Chunk should be placed in sector 10")
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_OK)

    def test053WriteNewChunkIncreaseFile(self):
        """
        write 3 sector chunk 2,2 (should go to 026-028 or 027-029) (increase file size)
        verify file size is 29*4096
        """
        nbt = generate_compressed_level(minsize = 9000, maxsize = 11000)
        self.region.write_chunk(1, 2, nbt)
        header = self.region.header[1, 2]
        self.assertEqual(header[1], 3, "Chunk length must be 3 sectors")
        self.assertIn(header[0], (26, 27), "Chunk should be placed in sector 26")
        self.assertEqual(self.region.get_size(), (header[0] + header[1])*4096, \
                         "File size should be multiple of 4096")
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_OK)

    def test054WriteExistingChunkDecreaseSector(self):
        """
        write 1 sector chunk 16,0 (should go to existing 017) (should free sector 018)
        write 1 sector chunk 1,2 (should go to 004)
        write 1 sector chunk 2,2 (should go to 010)
        write 1 sector chunk 3,2 (should go to 011)
        write 1 sector chunk 4,2 (should go to freed 018)
        write 1 sector chunk 5,2 (should go to 026)
        verify file size remains 027*4096
        """
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        header = self.region.header[16, 0]
        self.assertEqual(header[1], 2)
        self.region.write_chunk(16, 0, nbt)
        header = self.region.header[16, 0]
        self.assertEqual(header[1], 1, "Chunk length must be 1 sector1")
        self.assertEqual(header[0], 17, "Chunk should remain in sector 17")
        # Write 1-sector chunks to check which sectors are "free"
        locations = []
        self.region.write_chunk(1, 2, nbt)
        locations.append(self.region.header[1, 2][0])
        self.region.write_chunk(2, 2, nbt)
        locations.append(self.region.header[2, 2][0])
        self.region.write_chunk(3, 2, nbt)
        locations.append(self.region.header[3, 2][0])
        self.region.write_chunk(4, 2, nbt)
        locations.append(self.region.header[4, 2][0])
        self.region.write_chunk(5, 2, nbt)
        locations.append(self.region.header[5, 2][0])
        self.assertIn(18, locations)
        # self.assertEqual(locations, [4, 10, 11, 18, 26])
        # self.assertEqual(self.region.get_size(), 27*4096)

    @unittest.skip('Test takes too much time')
    def test055WriteChunkTooLarge(self):
        """
        Chunks of size >= 256 sectors are not supported by the file format
        attempt to write a chunk 256 sectors in size
        should raise Exception
        """
        maxsize = 256 * 4096
        nbt = generate_compressed_level(minsize = maxsize + 100, maxsize = maxsize + 4000)
        self.assertRaises(ChunkDataError, self.region.write_chunk, 2, 2, nbt)

    def test060WriteExistingChunkIncreaseSectorSameLocation(self):
        """
        write 2 sector chunk 7,0 (should go to 003-004) (increase chunk size)
        """
        nbt = generate_compressed_level(minsize = 5000, maxsize = 7000)
        self.region.write_chunk(7, 0, nbt)
        header = self.region.header[7, 0]
        self.assertEqual(header[1], 2, "Chunk length must be 2 sectors")
        self.assertEqual(header[0], 3, "Chunk should remain in sector 3")
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_OK)
        # self.assertEqual(self.region.get_size(), 27*4096)

    def test061WriteExistingChunkCorrectSize(self):
        """
        write 2 sector chunk 3,1 (should go to 025-026) (increase sector size)
        """
        nbt = self.region.get_chunk(3, 1)
        self.region.write_chunk(3, 1, nbt)
        header = self.region.header[3, 1]
        self.assertEqual(header[1], 2, "Chunk length must be 2 sectors")
        self.assertEqual(header[0], 25, "Chunk should remain in sector 25")
        self.assertEqual(header[3], RegionFile.STATUS_CHUNK_OK)
        self.assertEqual(self.region.get_size(), 27*4096)

    def test062WriteExistingChunkIncreaseSectorNewLocation(self):
        """
        write 2 sector chunk 8,0 (should go to 004-005 or 010-011)
        verify chunk_count remains 18
        write 2 sector chunk 2,2 (should go to 010-011 or 004-005)
        verify that file size is not increased <= 027*4096
        verify chunk_count is 19
        """
        locations = []
        chunk_count = self.region.chunk_count()
        nbt = generate_compressed_level(minsize = 5000, maxsize = 7000)
        self.region.write_chunk(8, 0, nbt)
        header = self.region.header[8, 0]
        self.assertEqual(header[1], 2) # length
        locations.append(header[0]) # location
        self.assertEqual(self.region.chunk_count(), chunk_count)
        self.region.write_chunk(2, 2, nbt)
        header = self.region.header[2, 2]
        self.assertEqual(header[1], 2) # length
        locations.append(header[0]) # location
        self.assertEqual(sorted(locations), [4, 10]) # locations
        self.assertEqual(self.region.chunk_count(), chunk_count + 1)

    def test063WriteNewChunkFreedSectors(self):
        """
        unlink chunk 6,0
        unlink chunk 7,0
        write 3 sector chunk 2,2 (should go to 002-004) (file size should remain the same)
        """
        self.region.unlink_chunk(6, 0)
        self.region.unlink_chunk(7, 0)
        nbt = generate_compressed_level(minsize = 9000, maxsize = 11000)
        self.region.write_chunk(2, 2, nbt)
        header = self.region.header[2, 2]
        self.assertEqual(header[1], 3, "Chunk length must be 3 sectors")
        self.assertEqual(header[0], 2, "Chunk should be placed in sector 2")

    # TODO: test write_blockdata, with different compressions. Check the metadata. Read the data back.

    def test070WriteOutOfFileChunk(self):
        """
        write 1 sector chunk 13,0 (should go to 004)
        Should not go to sector 30 (out-of-file location)
        """
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.region.write_chunk(13, 0, nbt)
        header = self.region.header[13, 0]
        self.assertEqual(header[1], 1) # length
        self.assertLessEqual(header[0], 26, \
                "Previously out-of-file chunk should be written in-file")

    def test071WriteZeroLengthSectorChunk(self):
        """
        write 1 sector chunk 13,0 (should go to 004)
        Verify sector 19 remains untouched.
        """
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.region.write_chunk(13, 0, nbt)
        header = self.region.header[13, 0]
        self.assertEqual(header[1], 1) # length
        self.assertNotEqual(header[0], 19, \
                "Previously 0-length chunk should not overwrite existing chunk")

    def test072WriteOverlappingChunkLong(self):
        """
        write 2 sector chunk 4,0 (should go to 010-011) (free 014 & 016)
        verify location is NOT 014 (because of overlap)
        write 1 sector chunk 1,2 (should go to 004)
        write 1 sector chunk 2,2 (should go to freed 014)
        write 1 sector chunk 3,2 (should go to freed 016)
        write 1 sector chunk 4,2 (should go to 018)
        write 1 sector chunk 5,2 (should go to 026)
        verify file size remains 027*4096
        """
        nbt = generate_compressed_level(minsize = 5000, maxsize = 7000)
        self.region.write_chunk(4, 0, nbt)
        header = self.region.header[4, 0]
        self.assertEqual(header[1], 2) # length
        self.assertNotEqual(header[0], 14, \
                "Chunk should not be written to same location when it overlaps")
        self.assertEqual(header[0], 10, \
                "Chunk should not be written to same location when it overlaps")
        # Write 1-sector chunks to check which sectors are "free"
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        locations = []
        self.region.write_chunk(1, 2, nbt)
        locations.append(self.region.header[1, 2][0])
        self.region.write_chunk(2, 2, nbt)
        locations.append(self.region.header[2, 2][0])
        self.region.write_chunk(3, 2, nbt)
        locations.append(self.region.header[3, 2][0])
        self.region.write_chunk(4, 2, nbt)
        locations.append(self.region.header[4, 2][0])
        self.region.write_chunk(5, 2, nbt)
        locations.append(self.region.header[5, 2][0])
        self.assertIn(14, locations)
        self.assertIn(16, locations)
        # self.assertEqual(locations, [4, 14, 16, 18, 26])
        # self.assertEqual(self.region.get_size(), 27*4096)

    def test073WriteOverlappingChunkSmall(self):
        """
        write 1 sector chunk 12,0 (should go to 004) ("free" 015 for use by 4,0)
        verify location is NOT 015
        verify sectors 15 and 16 are not marked as "free", but remain in use by 4,0
        """
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.region.write_chunk(12, 0, nbt)
        header = self.region.header[12, 0]
        self.assertEqual(header[1], 1) # length
        self.assertNotEqual(header[0], 15, \
                "Chunk should not be written to same location when it overlaps")
        # Write 1-sector chunks to check which sectors are "free"
        locations = []
        self.region.write_chunk(1, 2, nbt)
        locations.append(self.region.header[1, 2][0])
        self.region.write_chunk(2, 2, nbt)
        locations.append(self.region.header[2, 2][0])
        self.region.write_chunk(3, 2, nbt)
        locations.append(self.region.header[3, 2][0])
        self.assertNotIn(15, locations)
        self.assertNotIn(16, locations)
        # self.assertEqual(locations, [10, 11, 26])
        # self.assertEqual(self.region.get_size(), 27*4096)

    def test074WriteOverlappingChunkSameLocation(self):
        """
        write 1 sector chunk 12,0 (should go to 004) ("free" 012 for use by 4,0)
        write 3 sector chunk 4,0 (should stay in 014-016)
        verify file size remains <= 027*4096
        """
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.region.write_chunk(12, 0, nbt)
        header = self.region.header[12, 0]
        self.assertEqual(header[1], 1) # length
        self.assertNotEqual(header[0], 15, \
                "Chunk should not be written to same location when it overlaps")
        nbt = generate_compressed_level(minsize = 9000, maxsize = 11000)
        self.region.write_chunk(4, 0, nbt)
        header = self.region.header[4, 0]
        self.assertEqual(header[1], 3) # length
        self.assertEqual(header[0], 14, "No longer overlapping chunks " + \
                "should be written to same location when when possible")

    def test080FileTruncateLastChunkDecrease(self):
        """
        write 1 sector chunk 3,1 (should remain in 025) (free 026)
        verify file size is truncated: 26*4096 bytes
        """
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.region.write_chunk(3, 1, nbt)
        self.assertEqual(self.region.get_size(), 26*4096, \
                "File should be truncated when last chunk is reduced in size")

    def test081FileTruncateFreeTail(self):
        """
        delete chunk 3,1 (free 025: truncate file size)
        verify file size: 25*4096 bytes
        """
        self.region.unlink_chunk(3, 1)
        self.assertEqual(self.region.get_size(), 25*4096, \
                "File should be truncated when last sector(s) are freed")

    def test082FileTruncateMergeFree(self):
        """
        delete chunk 8,1 (free 024)
        delete chunk 3,1 (free 025: truncate file size, including 024)
        verify file size: 24*4096 bytes
        """
        self.region.unlink_chunk(8, 1)
        self.region.unlink_chunk(3, 1)
        self.assertEqual(self.region.get_size(), 24*4096, "File should be " + \
                "truncated as far as possible when last sector(s) are freed")

    def test090DeleteNonExistingChunk(self):
        """
        delete chunk 2,2
        """
        self.region.unlink_chunk(2, 2)
        self.assertFalse(self.region.metadata[2, 2].is_created())

    def test091DeleteNonInHeaderChunk(self):
        """
        delete chunk 14,0. This should leave sector 1 untouched.
        verify sector 1 is unmodified, with the exception of timestamp for chunk 14,0.
        """
        self.region.file.seek(4096)
        before = self.region.file.read(4096)
        chunklocation = 4 * (14 + 32*1)
        before = before[:chunklocation] + before[chunklocation+4:]
        self.region.unlink_chunk(14, 1)
        self.region.file.seek(4096)
        after = self.region.file.read(4096)
        after = after[:chunklocation] + after[chunklocation+4:]
        self.assertEqual(before, after)

    def test092DeleteOutOfFileChunk(self):
        """
        delete chunk 15,1
        verify file size is not increased.
        """
        size = self.region.get_size()
        self.region.unlink_chunk(15, 1)
        self.assertLessEqual(self.region.get_size(), size)

    def test093DeleteChunkZeroTimestamp(self):
        """
        delete chunk 17,0
        verify timestamp is zeroed. both in get_timestamp() and get_metadata()
        """
        self.assertEqual(self.region.get_timestamp(17, 0), 1334530101)
        self.region.unlink_chunk(17, 0)
        self.assertEqual(self.region.get_timestamp(17, 0), 0)

    def test100WriteZeroPadding(self):
        """
        write 1 sector chunk 16,0 (should go to existing 017) (should free sector 018)
        Check if unused bytes in sector 017 and all bytes in sector 018 are zeroed.
        """
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.region.write_chunk(16, 0, nbt)
        header = self.region.header[16, 0]
        chunk_header = self.region.chunk_headers[16, 0]
        sectorlocation = header[0]
        oldsectorlength = 2 * 4096
        chunklength = 4 + chunk_header[0]
        unusedlength = oldsectorlength - chunklength
        self.region.file.seek(4096*sectorlocation + chunklength)
        unused = self.region.file.read(unusedlength)
        zeroes = unused.count(b'\x00')
        self.assertEqual(zeroes, unusedlength, \
                "All unused bytes should be zeroed after writing a chunk")

    def test101DeleteZeroPadding(self):
        """
        unlink chunk 7,1
        Check if all bytes in sector 022 are zeroed.
        """
        header = self.region.header[7, 1]
        sectorlocation = header[0]
        self.region.unlink_chunk(7, 1)
        self.region.file.seek(sectorlocation*4096)
        unused = self.region.file.read(4096)
        zeroes = unused.count(b'\x00')
        self.assertEqual(zeroes, 4096, "All bytes should be zeroed after deleting a chunk")

    def test102DeleteOverlappingNoZeroPadding(self):
        """
        unlink chunk 4,0. Due to overlapping chunks, bytes should not be zeroed.
        Check if bytes in sector 015 are not all zeroed.
        """
        header = self.region.header[4, 0]
        sectorlocation = header[0]
        self.region.unlink_chunk(4, 0)
        self.region.file.seek((sectorlocation + 1) * 4096)
        unused = self.region.file.read(4096)
        zeroes = unused.count(b'\x00')
        self.assertNotEqual(zeroes, 4096, \
                "Bytes should not be zeroed after deleting an overlapping chunk")
        self.region.file.seek((sectorlocation) * 4096)
        unused = self.region.file.read(4096)
        zeroes = unused.count(b'\x00')
        self.assertEqual(zeroes, 4096, "Bytes should be " + \
                "zeroed after deleting non-overlapping portions of a chunk")
        self.region.file.seek((sectorlocation + 2) * 4096)
        unused = self.region.file.read(4096)
        zeroes = unused.count(b'\x00')
        self.assertEqual(zeroes, 4096, "Bytes should be " + \
                "zeroed after deleting non-overlapping portions of a chunk")

    def test103MoveOverlappingNoZeroPadding(self):
        """
        write 2 sector chunk 4,0 to a different location.
        Due to overlapping chunks, bytes should not be zeroed.
        Check if bytes in sector 015 are not all zeroed.
        """
        header = self.region.header[4, 0]
        sectorlocation = header[0]
        nbt = generate_compressed_level(minsize = 5000, maxsize = 7000)
        self.region.write_chunk(4, 0, nbt)
        self.region.file.seek((sectorlocation + 1) * 4096)
        unused = self.region.file.read(4096)
        zeroes = unused.count(b'\x00')
        self.assertNotEqual(zeroes, 4096, \
                "Bytes should not be zeroed after moving an overlapping chunk")

    def test104DeleteZeroPaddingMismatchLength(self):
        """
        unlink chunk 3,1. (which has a length mismatch)
        Check if bytes in sector 025 are all zeroed.
        Check if first byte in sector 026 is not zeroed.
        """
        raise unittest.SkipTest("Test can't use this testfile")
        # TODO: Why not? Create other test file

class EmptyFileTest(unittest.TestCase):
    """Test for 0-byte file support.
    These files should be treated as a valid region file without any stored chunk."""

    @staticmethod
    def generate_level():
        level = NBTFile() # Blank NBT
        level.name = "Data"
        level.tags.extend([
            TAG_Long(name="Time", value=1),
            TAG_Long(name="LastPlayed", value=1376031942),
            TAG_Int(name="SpawnX", value=0),
            TAG_Int(name="SpawnY", value=2),
            TAG_Int(name="SpawnZ", value=0),
            TAG_Long(name="SizeOnDisk", value=0),
            TAG_Long(name="RandomSeed", value=123456789),
            TAG_Int(name="version", value=19132),
            TAG_String(name="LevelName", value="Testing")
        ])
        player = TAG_Compound()
        player.name = "Player"
        player.tags.extend([
            TAG_Int(name="Score", value=0),
            TAG_Int(name="Dimension", value=0)
        ])
        inventory = TAG_Compound()
        inventory.name = "Inventory"
        player.tags.append(inventory)
        level.tags.append(player)
        return level

    def setUp(self):
        self.stream = PedanticFileWrapper(BytesIO(b""))
        # self.stream.seek(0)

    def test01ReadFile(self):
        region = RegionFile(fileobj=self.stream)
        self.assertEqual(region.chunk_count(), 0)

    def test02WriteFile(self):
        chunk = self.generate_level()
        region = RegionFile(fileobj=self.stream)
        region.write_chunk(0, 0, chunk)
        self.assertEqual(region.get_size(), 3*4096)
        self.assertEqual(region.chunk_count(), 1)

class RegionFileInitTest(unittest.TestCase):
    """Tests for the various init parameters provided for RegionFile()."""

    def testCreateFromFilename(self):
        """
        Creating a RegionFile with filename, and deleting the instance should
        close the underlying file.
        """
        tempdir = tempfile.mkdtemp()
        filename = os.path.join(tempdir, 'regiontest.mca')
        shutil.copy(REGIONTESTFILE, filename)
        try:
            openfiles_before = open_files()
        except OSError:
            raise unittest.SkipTest("Can't get a list of open files")
        region = RegionFile(filename = filename)
        openfiles_during = open_files()
        del region
        openfiles_after = open_files()

        self.assertNotEqual(len(openfiles_during), 0)
        self.assertNotEqual(openfiles_before, openfiles_during)
        self.assertEqual(openfiles_before, openfiles_after, "File is not closed")

    def testCreateFromFileobject(self):
        """
        Creating RegionFile with file object, and deleting the instance should
        not close the underlying file.
        """
        tempdir = tempfile.mkdtemp()
        filename = os.path.join(tempdir, 'regiontest.mca')
        shutil.copy(REGIONTESTFILE, filename)
        fileobj = open(filename, "r+b")

        try:
            openfiles_before = open_files()
        except OSError:
            raise unittest.SkipTest("Can't get a list of open files")
        region = RegionFile(fileobj = fileobj)
        openfiles_during = open_files()
        del region
        openfiles_after = open_files()

        fileobj.close()

        self.assertNotEqual(len(openfiles_during), 0)
        self.assertEqual(openfiles_before, openfiles_during)
        self.assertEqual(openfiles_before, openfiles_after, \
                "File is closed, while it should remain open")

    def testNoParameters(self):
        """Calling RegionFile without parameters should raise a ValueError"""
        # Equivalent to: region = RegionFile()
        self.assertRaises(ValueError, RegionFile)

    def testTwoParameters(self):
        """Calling RegionFile with both filename and fileobject should ignore the fileobject."""
        tempdir = tempfile.mkdtemp()
        filename_name = os.path.join(tempdir, 'regiontest_name.mca')
        shutil.copy(REGIONTESTFILE, filename_name)
        filename_obj = os.path.join(tempdir, 'regiontest_obj.mca')
        shutil.copy(REGIONTESTFILE, filename_obj)
        fileobj = open(filename_obj, "r+b")

        try:
            openfiles_before = open_files()
        except OSError:
            raise unittest.SkipTest("Can't get a list of open files")
        region = RegionFile(filename = filename_name, fileobj = fileobj)
        openfiles_during = open_files()
        self.assertEqual(region.filename, region.file.name)
        self.assertEqual(region.filename, filename_name)
        del region
        openfiles_after = open_files()

        fileobj.close()
        self.assertEqual(openfiles_before, openfiles_after)

# TODO: write tests
# class PartialHeaderFileTest(EmptyFileTest):
#   """Test for file support with only a partial header file.
#   These files should be treated as a valid region file without any stored chunk."""
#
#   def setUp(self):
#       self.stream = BytesIO(4096*b"\x00" + b"\x52\x1E\x8B\xE6")
#       self.stream.seek(0)
#
#   def test03GetTimestampNoChunk(self):
#       region = RegionFile(fileobj=self.stream)
#       self.assertEqual(region.get_timestamp(0,0), 1377733606)

class TruncatedFileTest(unittest.TestCase):
    """Test for truncated file support.
    This files should be treated as a valid region file, as all data is present.
    Only the padding bytes are missing."""

    def setUp(self):
        # data contains 8235, just over 2 sectors.
        # Only chunk (0,0) is stored, at sector 2, with length 1.
        # The timestamps are all zeroed.
        # The chunk is 0x27 = 39 bytes, excl header. Compression type 2, zlib.
        data = b'\x00\x00\x02\x01' + 8188*b'\x00' + \
               b'\x00\x00\x00\x27\x02' + \
               b'\x78\xda\xe3\x62\x60\x71\x49\x2c\x49\x64\x61\x60\x09\xc9' + \
               b'\xcc\x4d\x65\x80\x00\x46\x0e\x06\x16\xbf\x44\x20\x97\x25' + \
               b'\x24\xb5\xb8\x84\x01\x00\x6b\xb7\x06\x52'
        self.length = 8235 # 4096 + 4096 + 4 + 39
        self.assertEqual(len(data), self.length)
        stream = PedanticFileWrapper(BytesIO(data))
        # stream.seek(0)
        self.region = RegionFile(fileobj=stream)

    def tearDown(self):
        del self.region

    def test00FileProperties(self):
        self.assertEqual(self.region.get_size(), self.length)
        self.assertEqual(self.region.chunk_count(), 1)

    def test01ReadChunk(self):
        """Test if a block can be read, even when the file is truncated right
        after the block data."""
        data = self.region.get_blockdata(0,0) # This may raise a RegionFileFormatError.
        data = BytesIO(data)
        nbt = NBTFile(buffer=data)
        self.assertEqual(nbt["Time"].value, 1)
        self.assertEqual(nbt["Name"].value, "Test")

    def test02ReplaceChunk(self):
        """Test if writing the last block in a truncated file will extend the
        file size to the sector boundary."""
        nbt = self.region.get_nbt(0, 0)
        self.region.write_chunk(0, 0, nbt)
        size = self.region.size
        self.assertEqual(size, self.region.get_size())
        self.assertEqual(size, 3*4096)

    def test03WriteChunk(self):
        """Test if a new chunk extends the file to sector sizes."""
        nbt = generate_compressed_level(minsize = 100, maxsize = 4000)
        self.region.write_chunk(0, 1, nbt)
        self.assertEqual(self.region.get_size(), 4*4096)
        self.assertEqual(self.region.size, 4*4096)
        self.assertEqual(self.region.chunk_count(), 2)
        self.region.file.seek(self.length)
        # The file length was extended to a block length.
        # Test if the padding contains zeroes.
        unusedlength = 3*4096 - self.length
        unused = self.region.file.read(unusedlength)
        zeroes = unused.count(b'\x00')
        self.assertEqual(unusedlength, zeroes)

class LengthTest(unittest.TestCase):
    """Test for length calculations for blocks with inconsistent lengths.

    This operates on a simple testfile with:
    file length: 4 sectors (00-03, inclusive)
    chunk 0,0: header length 4 sectors; chunk length 10240 bytes (3 sectors); sector 02-05
    chunk 1,0: header length 3 sectors; chunk length 613566756 bytes (149797 sectors); sector 03-149799
    """
    # max length value in header: 255 sectors = 1044480 bytes (1 MiByte)
    # max length value in chunk: 4294967295 bytes (4 GiBye) = 1048576 sectors
    def setUp(self):
        data = b'\x00\x00\x02\x04\x00\x00\x03\x03' + 8184*b'\x00' + \
               b'\x00\x00\x28\x00\x00' + 4091*b'\x01' + \
               b'\x24\x92\x49\x24\x00' + 4091*b'\x02'
        self.length = 16384
        self.assertEqual(len(data), self.length)
        stream = PedanticFileWrapper(BytesIO(data))
        # stream.seek(0)
        self.region = RegionFile(fileobj=stream)

    def tearDown(self):
        del self.region

    def test00FileProperties(self):
        self.assertEqual(self.region.get_size(), self.length)
        self.assertEqual(self.region.chunk_count(), 2)

    def testSectors(self):
        """Test if RegionFile._sectors() detects the correct overlap."""
        sectors = self.region._sectors()
        chunk00metadata = self.region.metadata[0,0]
        chunk10metadata = self.region.metadata[1,0]
        self.assertEqual(len(sectors), 4)
        self.assertEqual(sectors[0], True)
        self.assertEqual(sectors[1], True)
        self.assertEqual(len(sectors[2]), 1)
        self.assertIn(chunk00metadata, sectors[2])
        self.assertNotIn(chunk10metadata, sectors[2])
        self.assertEqual(len(sectors[3]), 2)
        self.assertIn(chunk00metadata, sectors[3])
        self.assertIn(chunk10metadata, sectors[3])

    def testMetaDataLengths(self):
        chunk00metadata = self.region.metadata[0,0]
        chunk10metadata = self.region.metadata[1,0]
        self.assertEqual(chunk00metadata.blocklength, 4)
        self.assertEqual(chunk00metadata.length, 10240)
        self.assertEqual(chunk10metadata.blocklength, 3)
        self.assertEqual(chunk10metadata.length, 613566756)

    def testMetaDataLengthCalculations(self):
        chunk00metadata = self.region.metadata[0,0]
        chunk10metadata = self.region.metadata[1,0]
        self.assertEqual(chunk00metadata.requiredblocks(), 3)
        self.assertEqual(chunk10metadata.requiredblocks(), 149797)

    def testMetaDataStatus(self):
        # performa low-level read, ensure it does not read past the file length
        # and does not modify the file
        chunk00metadata = self.region.metadata[0,0]
        chunk10metadata = self.region.metadata[1,0]
        self.assertIn(chunk00metadata.status,
                    (RegionFile.STATUS_CHUNK_OVERLAPPING,
                     RegionFile.STATUS_CHUNK_OUT_OF_FILE))
        self.assertIn(chunk10metadata.status,
                    (RegionFile.STATUS_CHUNK_MISMATCHED_LENGTHS,
                     RegionFile.STATUS_CHUNK_OVERLAPPING,
                     RegionFile.STATUS_CHUNK_OUT_OF_FILE))

    def testChunkRead(self):
        """
        Perform a low-level read, ensure it does not read past the file length
        and does not modify the file.
        """
        # Does not raise a ChunkDataError(), since the data can be read,
        # even though it is shorter than specified in the header.
        data = self.region.get_blockdata(0, 0)
        self.assertEqual(len(data), 8187)
        data = self.region.get_blockdata(1, 0)
        self.assertEqual(len(data), 4091)
        self.assertEqual(self.region.get_size(), self.length)

    def testDeleteChunk(self):
        """Try to remove the chunk 1,0 with ridiculous large size.
        This should be reasonably fast."""
        self.region.unlink_chunk(1, 0)
        self.assertEqual(self.region.chunk_count(), 1)

# TODO: check if metadata is updated after deleting or writing a chunk
# TODO: in tests, replace region.header or region.chunk_headers with region.metadata

if __name__ == '__main__':
    logger = logging.getLogger("nbt.tests.regiontests")
    if len(logger.handlers) == 0:
        # Logging is not yet configured. Configure it.
        logging.basicConfig(level=logging.INFO, stream=sys.stderr, format='%(levelname)-8s %(message)s')
    unittest.main()

redpajama_set_name: RedPajamaGithub