Compare commits
56 Commits
0.7.5
...
a0510eaec1
| Author | SHA1 | Date | |
|---|---|---|---|
| a0510eaec1 | |||
| cce2c68cab | |||
| 3c5f06f41d | |||
| 6bc5381108 | |||
| ef51863180 | |||
| 09062351f5 | |||
| ab4137572e | |||
| c03a7ec18f | |||
|
|
df298f187e | ||
| 466c72924a | |||
| 9dfb803ba8 | |||
| de46a0c568 | |||
| d9ba67b6f0 | |||
| 9342abeb7a | |||
| e8cbc632c9 | |||
| c556a3d3e4 | |||
| 3aee719130 | |||
| 10662ee803 | |||
| 672875f48f | |||
| 3e6d93182a | |||
| 1c95c9f8f9 | |||
| 0ec2cf8ca5 | |||
| 9ac10a6a7d | |||
| 1a2c77c6a6 | |||
| 3baecb7b51 | |||
| 5d5b0ae3c1 | |||
| f47c8edb42 | |||
| ef66a24f87 | |||
| f6a5cb8668 | |||
| 4dec385332 | |||
| 5b08e14b55 | |||
| ac381cbf60 | |||
| ce7fdcdcd6 | |||
| a31c9d1e88 | |||
| a5942ff026 | |||
| 4d71ad08ce | |||
| 92017ab630 | |||
| 16af98b32d | |||
| 6da01e382f | |||
| ed06543981 | |||
| d3eb2fc254 | |||
| a1eb06324f | |||
| d12ce30f57 | |||
| 7a6f0e8728 | |||
| 7ed5829d47 | |||
| d708f80a1f | |||
| f07619b7c6 | |||
| c5bcb90b65 | |||
| 4f047dbc77 | |||
| bc348c7dd5 | |||
| 5d1c5cc2ce | |||
| 8f6ca81d22 | |||
| a27d4ed55d | |||
| 64ae6db83a | |||
| 2a0a88a96d | |||
| 69dbf6714d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,3 +10,5 @@ tags
|
||||
.stack-work/
|
||||
.cabal-sandbox/
|
||||
cabal.sandbox.config
|
||||
dist-newstyle/
|
||||
.ghc.environment.*
|
||||
|
||||
@@ -7,12 +7,18 @@ dist: trusty
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: CABALVER=1.18 GHCVER=7.6.3
|
||||
addons: {apt: {packages: [cabal-install-1.18,ghc-7.6.3], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=1.22 GHCVER=7.8.4
|
||||
addons: {apt: {packages: [cabal-install-1.22,ghc-7.8.4], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=1.24 GHCVER=7.10.2
|
||||
addons: {apt: {packages: [cabal-install-1.24,ghc-7.10.2], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=1.24 GHCVER=8.0.1
|
||||
addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=2.0 GHCVER=8.2.2
|
||||
addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=2.2 GHCVER=8.4.1
|
||||
addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.1], sources: [hvr-ghc]}}
|
||||
- env: CABALVER=head GHCVER=head
|
||||
addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}}
|
||||
|
||||
|
||||
19
CHANGELOG
19
CHANGELOG
@@ -1,3 +1,22 @@
|
||||
0.9.2
|
||||
* fix build with ghc-7.6
|
||||
* raise required bytestring version
|
||||
* Tighten base bound to prevent building before GHC 7.6 (by George Wilson)
|
||||
0.9.1
|
||||
* fix build with ghc-7.8 and 7.10
|
||||
0.9.0
|
||||
* don't force "Path Abs" anymore in IO module, abstract more over Path types
|
||||
* add 'toAbs'
|
||||
0.8.1
|
||||
* add 'readFile', 'readFileEOF', 'writeFile' and 'appendFile'
|
||||
0.8.0
|
||||
* 'copyDirRecursiveOverwrite', 'copyFileOverwrite', 'easyCopyOverwrite' and 'moveFileOverwrite' have been removed, instead use the versions without the *Overwrite suffix and pass in 'Strict' (for default behavior) or 'Overwrite' as the CopyMode argument
|
||||
* introduced a new 'RecursiveErrorMode' type to allow controlling recursive behavior of 'copyDirRecursive' (use 'FailEarly' for default behavior)
|
||||
* 'createRegularFile' and 'createDir' now take FileMode as a parameter (also see 'newFilePerms' and 'newDirPerms')
|
||||
* various documentation fixes
|
||||
* improved reliability of tests
|
||||
0.7.5:
|
||||
* relicense to BSD3
|
||||
0.7.3:
|
||||
* don't expose HPath.Internal
|
||||
0.7.2:
|
||||
|
||||
358
LICENSE
358
LICENSE
@@ -1,340 +1,30 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
Copyright (c) Julian Ospald
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
All rights reserved.
|
||||
|
||||
Preamble
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
3. Neither the name of the author nor the names of his contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# HPath
|
||||
|
||||
[](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hackage.haskell.org/package/hpath) [](http://travis-ci.org/hasufell/hpath)
|
||||
[](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hackage.haskell.org/package/hpath) [](http://travis-ci.org/hasufell/hpath) [](http://packdeps.haskellers.com/feed?needle=hpath)
|
||||
|
||||
Support for well-typed paths in Haskell. Also provides ByteString based filepath
|
||||
manipulation.
|
||||
|
||||
42
hpath.cabal
42
hpath.cabal
@@ -1,15 +1,15 @@
|
||||
name: hpath
|
||||
version: 0.7.3
|
||||
version: 0.9.2
|
||||
synopsis: Support for well-typed paths
|
||||
description: Support for well-typed paths, utilizing ByteString under the hood.
|
||||
license: GPL-2
|
||||
license: BSD3
|
||||
license-file: LICENSE
|
||||
author: Julian Ospald <hasufell@posteo.de>
|
||||
maintainer: Julian Ospald <hasufell@posteo.de>
|
||||
copyright: Julian Ospald 2016
|
||||
category: Filesystem
|
||||
build-type: Simple
|
||||
cabal-version: >=1.14
|
||||
cabal-version: 1.14
|
||||
extra-source-files: README.md
|
||||
CHANGELOG
|
||||
cbits/dirutils.h
|
||||
@@ -17,21 +17,27 @@ extra-source-files: README.md
|
||||
doctests-posix.hs
|
||||
|
||||
library
|
||||
if os(windows)
|
||||
build-depends: unbuildable<0
|
||||
buildable: False
|
||||
hs-source-dirs: src/
|
||||
default-language: Haskell2010
|
||||
ghc-options: -Wall
|
||||
if impl(ghc >= 8.0)
|
||||
ghc-options: -Wall -Wno-redundant-constraints
|
||||
else
|
||||
ghc-options: -Wall
|
||||
c-sources: cbits/dirutils.c
|
||||
exposed-modules: HPath,
|
||||
HPath.IO,
|
||||
HPath.IO.Errors,
|
||||
HPath.IO.Utils,
|
||||
System.Posix.Directory.Foreign,
|
||||
System.Posix.Directory.Traversals,
|
||||
System.Posix.FD,
|
||||
System.Posix.FilePath
|
||||
other-modules: HPath.Internal
|
||||
build-depends: base >= 4.2 && <5
|
||||
, bytestring >= 0.9.2.0
|
||||
build-depends: base >= 4.6 && <5
|
||||
, IfElse
|
||||
, bytestring >= 0.10.0.0
|
||||
, deepseq
|
||||
, exceptions
|
||||
, hspec
|
||||
@@ -43,6 +49,9 @@ library
|
||||
|
||||
|
||||
test-suite doctests-hpath
|
||||
if os(windows)
|
||||
build-depends: unbuildable<0
|
||||
buildable: False
|
||||
default-language: Haskell2010
|
||||
type: exitcode-stdio-1.0
|
||||
ghc-options: -threaded
|
||||
@@ -54,12 +63,15 @@ test-suite doctests-hpath
|
||||
, hpath
|
||||
|
||||
test-suite doctests-posix
|
||||
if os(windows)
|
||||
build-depends: unbuildable<0
|
||||
buildable: False
|
||||
default-language: Haskell2010
|
||||
type: exitcode-stdio-1.0
|
||||
ghc-options: -threaded
|
||||
main-is: doctests-posix.hs
|
||||
build-depends: base,
|
||||
bytestring,
|
||||
bytestring >= 0.10.0.0,
|
||||
unix,
|
||||
hpath,
|
||||
doctest >= 0.8,
|
||||
@@ -67,16 +79,22 @@ test-suite doctests-posix
|
||||
QuickCheck
|
||||
|
||||
test-suite spec
|
||||
if os(windows)
|
||||
build-depends: unbuildable<0
|
||||
buildable: False
|
||||
Type: exitcode-stdio-1.0
|
||||
Default-Language: Haskell2010
|
||||
Hs-Source-Dirs: test
|
||||
Main-Is: Main.hs
|
||||
other-modules:
|
||||
HPath.IO.AppendFileSpec
|
||||
HPath.IO.CanonicalizePathSpec
|
||||
HPath.IO.CopyDirRecursiveCollectFailuresSpec
|
||||
HPath.IO.CopyDirRecursiveOverwriteSpec
|
||||
HPath.IO.CopyDirRecursiveSpec
|
||||
HPath.IO.CopyFileOverwriteSpec
|
||||
HPath.IO.CopyFileSpec
|
||||
HPath.IO.CreateDirRecursiveSpec
|
||||
HPath.IO.CreateDirSpec
|
||||
HPath.IO.CreateRegularFileSpec
|
||||
HPath.IO.CreateSymlinkSpec
|
||||
@@ -87,14 +105,20 @@ test-suite spec
|
||||
HPath.IO.GetFileTypeSpec
|
||||
HPath.IO.MoveFileOverwriteSpec
|
||||
HPath.IO.MoveFileSpec
|
||||
HPath.IO.ReadFileEOFSpec
|
||||
HPath.IO.ReadFileSpec
|
||||
HPath.IO.RecreateSymlinkOverwriteSpec
|
||||
HPath.IO.RecreateSymlinkSpec
|
||||
HPath.IO.RenameFileSpec
|
||||
HPath.IO.ToAbsSpec
|
||||
HPath.IO.WriteFileSpec
|
||||
Spec
|
||||
Utils
|
||||
GHC-Options: -Wall
|
||||
Build-Depends: base
|
||||
, HUnit
|
||||
, bytestring
|
||||
, IfElse
|
||||
, bytestring >= 0.10.0.0
|
||||
, hpath
|
||||
, hspec >= 1.3
|
||||
, process
|
||||
|
||||
12
src/HPath.hs
12
src/HPath.hs
@@ -13,7 +13,9 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
{-# LANGUAGE EmptyDataDecls #-}
|
||||
#if __GLASGOW_HASKELL__ >= 708
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
#endif
|
||||
|
||||
module HPath
|
||||
(
|
||||
@@ -25,8 +27,10 @@ module HPath
|
||||
,PathParseException
|
||||
,PathException
|
||||
,RelC
|
||||
#if __GLASGOW_HASKELL__ >= 708
|
||||
-- * PatternSynonyms/ViewPatterns
|
||||
,pattern Path
|
||||
#endif
|
||||
-- * Path Parsing
|
||||
,parseAbs
|
||||
,parseFn
|
||||
@@ -101,7 +105,9 @@ instance RelC Fn
|
||||
#if __GLASGOW_HASKELL__ >= 710
|
||||
pattern Path :: ByteString -> Path a
|
||||
#endif
|
||||
#if __GLASGOW_HASKELL__ >= 708
|
||||
pattern Path x <- (MkPath x)
|
||||
#endif
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Path Parsers
|
||||
@@ -314,10 +320,6 @@ getAllParents (MkPath p)
|
||||
|
||||
-- | Extract the directory name of a path.
|
||||
--
|
||||
-- The following properties hold:
|
||||
--
|
||||
-- @dirname (p \<\/> a) == dirname p@
|
||||
--
|
||||
-- >>> dirname (MkPath "/abc/def/dod")
|
||||
-- "/abc/def"
|
||||
-- >>> dirname (MkPath "/")
|
||||
@@ -336,6 +338,8 @@ dirname (MkPath fp) = MkPath (takeDirectory $ dropTrailingPathSeparator fp)
|
||||
--
|
||||
-- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Fn)
|
||||
-- Just "dod"
|
||||
-- >>> basename (MkPath "/abc/def/dod/") :: Maybe (Path Fn)
|
||||
-- Just "dod"
|
||||
-- >>> basename (MkPath "/") :: Maybe (Path Fn)
|
||||
-- Nothing
|
||||
basename :: MonadThrow m => Path b -> m (Path Fn)
|
||||
|
||||
759
src/HPath/IO.hs
759
src/HPath/IO.hs
File diff suppressed because it is too large
Load Diff
@@ -3,5 +3,6 @@ module HPath.IO where
|
||||
|
||||
import HPath
|
||||
|
||||
canonicalizePath :: Path Abs -> IO (Path Abs)
|
||||
canonicalizePath :: Path b -> IO (Path Abs)
|
||||
|
||||
toAbs :: Path b -> IO (Path Abs)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- |
|
||||
-- Module : HPath.IO.Errors
|
||||
-- Copyright : © 2016 Julian Ospald
|
||||
-- License : GPL-2
|
||||
-- License : BSD3
|
||||
--
|
||||
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
||||
-- Stability : experimental
|
||||
@@ -16,23 +16,20 @@ module HPath.IO.Errors
|
||||
(
|
||||
-- * Types
|
||||
HPathIOException(..)
|
||||
, RecursiveFailureHint(..)
|
||||
|
||||
-- * Exception identifiers
|
||||
, isFileDoesNotExist
|
||||
, isDirDoesNotExist
|
||||
, isSameFile
|
||||
, isDestinationInSource
|
||||
, isFileDoesExist
|
||||
, isDirDoesExist
|
||||
, isInvalidOperation
|
||||
, isCan'tOpenDirectory
|
||||
, isCopyFailed
|
||||
, isRecursiveFailure
|
||||
, isReadContentsFailed
|
||||
, isCreateDirFailed
|
||||
, isCopyFileFailed
|
||||
, isRecreateSymlinkFailed
|
||||
|
||||
-- * Path based functions
|
||||
, throwFileDoesExist
|
||||
, throwDirDoesExist
|
||||
, throwFileDoesNotExist
|
||||
, throwDirDoesNotExist
|
||||
, throwSameFile
|
||||
, sameFile
|
||||
, throwDestinationInSource
|
||||
@@ -40,7 +37,6 @@ module HPath.IO.Errors
|
||||
, doesDirectoryExist
|
||||
, isWritable
|
||||
, canOpenDirectory
|
||||
, throwCantOpenDirectory
|
||||
|
||||
-- * Error handling functions
|
||||
, catchErrno
|
||||
@@ -62,6 +58,10 @@ import Control.Monad
|
||||
forM
|
||||
, when
|
||||
)
|
||||
import Control.Monad.IfElse
|
||||
(
|
||||
whenM
|
||||
)
|
||||
import Data.ByteString
|
||||
(
|
||||
ByteString
|
||||
@@ -70,11 +70,10 @@ import Data.ByteString.UTF8
|
||||
(
|
||||
toString
|
||||
)
|
||||
import Data.Data
|
||||
(
|
||||
Data(..)
|
||||
)
|
||||
import Data.Typeable
|
||||
(
|
||||
Typeable
|
||||
)
|
||||
import Foreign.C.Error
|
||||
(
|
||||
getErrno
|
||||
@@ -85,15 +84,21 @@ import GHC.IO.Exception
|
||||
IOErrorType
|
||||
)
|
||||
import HPath
|
||||
import HPath.Internal
|
||||
(
|
||||
Path(..)
|
||||
)
|
||||
import {-# SOURCE #-} HPath.IO
|
||||
(
|
||||
canonicalizePath
|
||||
, toAbs
|
||||
)
|
||||
import HPath.IO.Utils
|
||||
import System.IO.Error
|
||||
(
|
||||
catchIOError
|
||||
alreadyExistsErrorType
|
||||
, catchIOError
|
||||
, ioeGetErrorType
|
||||
, mkIOError
|
||||
)
|
||||
|
||||
import qualified System.Posix.Directory.ByteString as PFD
|
||||
@@ -105,40 +110,36 @@ import System.Posix.Files.ByteString
|
||||
import qualified System.Posix.Files.ByteString as PF
|
||||
|
||||
|
||||
data HPathIOException = FileDoesNotExist ByteString
|
||||
| DirDoesNotExist ByteString
|
||||
| SameFile ByteString ByteString
|
||||
-- |Additional generic IO exceptions that the posix functions
|
||||
-- do not provide.
|
||||
data HPathIOException = SameFile ByteString ByteString
|
||||
| DestinationInSource ByteString ByteString
|
||||
| FileDoesExist ByteString
|
||||
| DirDoesExist ByteString
|
||||
| InvalidOperation String
|
||||
| Can'tOpenDirectory ByteString
|
||||
| CopyFailed String
|
||||
deriving (Typeable, Eq, Data)
|
||||
| RecursiveFailure [(RecursiveFailureHint, IOException)]
|
||||
deriving (Eq, Show, Typeable)
|
||||
|
||||
|
||||
instance Show HPathIOException where
|
||||
show (FileDoesNotExist fp) = "File does not exist:" ++ toString fp
|
||||
show (DirDoesNotExist fp) = "Directory does not exist: "
|
||||
++ toString fp
|
||||
show (SameFile fp1 fp2) = toString fp1
|
||||
++ " and " ++ toString fp2
|
||||
++ " are the same file!"
|
||||
show (DestinationInSource fp1 fp2) = toString fp1
|
||||
++ " is contained in "
|
||||
++ toString fp2
|
||||
show (FileDoesExist fp) = "File does exist: " ++ toString fp
|
||||
show (DirDoesExist fp) = "Directory does exist: " ++ toString fp
|
||||
show (InvalidOperation str) = "Invalid operation: " ++ str
|
||||
show (Can'tOpenDirectory fp) = "Can't open directory: "
|
||||
++ toString fp
|
||||
show (CopyFailed str) = "Copying failed: " ++ str
|
||||
|
||||
-- |A type for giving failure hints on recursive failure, which allows
|
||||
-- to programmatically make choices without examining
|
||||
-- the weakly typed I/O error attributes (like `ioeGetFileName`).
|
||||
--
|
||||
-- The first argument to the data constructor is always the
|
||||
-- source and the second the destination.
|
||||
data RecursiveFailureHint = ReadContentsFailed ByteString ByteString
|
||||
| CreateDirFailed ByteString ByteString
|
||||
| CopyFileFailed ByteString ByteString
|
||||
| RecreateSymlinkFailed ByteString ByteString
|
||||
deriving (Eq, Show)
|
||||
|
||||
|
||||
instance Exception HPathIOException
|
||||
|
||||
|
||||
toConstr :: HPathIOException -> String
|
||||
toConstr SameFile {} = "SameFile"
|
||||
toConstr DestinationInSource {} = "DestinationInSource"
|
||||
toConstr RecursiveFailure {} = "RecursiveFailure"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -146,16 +147,23 @@ instance Exception HPathIOException
|
||||
--[ Exception identifiers ]--
|
||||
-----------------------------
|
||||
|
||||
isFileDoesNotExist, isDirDoesNotExist, isSameFile, isDestinationInSource, isFileDoesExist, isDirDoesExist, isInvalidOperation, isCan'tOpenDirectory, isCopyFailed :: HPathIOException -> Bool
|
||||
isFileDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr FileDoesNotExist{}
|
||||
isDirDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesNotExist{}
|
||||
|
||||
isSameFile, isDestinationInSource, isRecursiveFailure :: HPathIOException -> Bool
|
||||
isSameFile ex = toConstr (ex :: HPathIOException) == toConstr SameFile{}
|
||||
isDestinationInSource ex = toConstr (ex :: HPathIOException) == toConstr DestinationInSource{}
|
||||
isFileDoesExist ex = toConstr (ex :: HPathIOException) == toConstr FileDoesExist{}
|
||||
isDirDoesExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesExist{}
|
||||
isInvalidOperation ex = toConstr (ex :: HPathIOException) == toConstr InvalidOperation{}
|
||||
isCan'tOpenDirectory ex = toConstr (ex :: HPathIOException) == toConstr Can'tOpenDirectory{}
|
||||
isCopyFailed ex = toConstr (ex :: HPathIOException) == toConstr CopyFailed{}
|
||||
isRecursiveFailure ex = toConstr (ex :: HPathIOException) == toConstr RecursiveFailure{}
|
||||
|
||||
|
||||
isReadContentsFailed, isCreateDirFailed, isCopyFileFailed, isRecreateSymlinkFailed ::RecursiveFailureHint -> Bool
|
||||
isReadContentsFailed ReadContentsFailed{} = True
|
||||
isReadContentsFailed _ = False
|
||||
isCreateDirFailed CreateDirFailed{} = True
|
||||
isCreateDirFailed _ = False
|
||||
isCopyFileFailed CopyFileFailed{} = True
|
||||
isCopyFileFailed _ = False
|
||||
isRecreateSymlinkFailed RecreateSymlinkFailed{} = True
|
||||
isRecreateSymlinkFailed _ = False
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -165,52 +173,51 @@ isCopyFailed ex = toConstr (ex :: HPathIOException) == toConstr CopyFailed{}
|
||||
----------------------------
|
||||
|
||||
|
||||
throwFileDoesExist :: Path Abs -> IO ()
|
||||
throwFileDoesExist fp =
|
||||
whenM (doesFileExist fp) (throwIO . FileDoesExist
|
||||
. fromAbs $ fp)
|
||||
-- |Throws `AlreadyExists` `IOError` if file exists.
|
||||
throwFileDoesExist :: Path b -> IO ()
|
||||
throwFileDoesExist fp@(MkPath bs) =
|
||||
whenM (doesFileExist fp)
|
||||
(ioError . mkIOError
|
||||
alreadyExistsErrorType
|
||||
"File already exists"
|
||||
Nothing
|
||||
$ (Just (toString $ bs))
|
||||
)
|
||||
|
||||
|
||||
throwDirDoesExist :: Path Abs -> IO ()
|
||||
throwDirDoesExist fp =
|
||||
whenM (doesDirectoryExist fp) (throwIO . DirDoesExist
|
||||
. fromAbs $ fp)
|
||||
|
||||
|
||||
throwFileDoesNotExist :: Path Abs -> IO ()
|
||||
throwFileDoesNotExist fp =
|
||||
unlessM (doesFileExist fp) (throwIO . FileDoesNotExist
|
||||
. fromAbs $ fp)
|
||||
|
||||
|
||||
throwDirDoesNotExist :: Path Abs -> IO ()
|
||||
throwDirDoesNotExist fp =
|
||||
unlessM (doesDirectoryExist fp) (throwIO . DirDoesNotExist
|
||||
. fromAbs $ fp)
|
||||
-- |Throws `AlreadyExists` `IOError` if directory exists.
|
||||
throwDirDoesExist :: Path b -> IO ()
|
||||
throwDirDoesExist fp@(MkPath bs) =
|
||||
whenM (doesDirectoryExist fp)
|
||||
(ioError . mkIOError
|
||||
alreadyExistsErrorType
|
||||
"Directory already exists"
|
||||
Nothing
|
||||
$ (Just (toString $ bs))
|
||||
)
|
||||
|
||||
|
||||
-- |Uses `isSameFile` and throws `SameFile` if it returns True.
|
||||
throwSameFile :: Path Abs
|
||||
-> Path Abs
|
||||
throwSameFile :: Path b1
|
||||
-> Path b2
|
||||
-> IO ()
|
||||
throwSameFile fp1 fp2 =
|
||||
throwSameFile fp1@(MkPath bs1) fp2@(MkPath bs2) =
|
||||
whenM (sameFile fp1 fp2)
|
||||
(throwIO $ SameFile (fromAbs fp1) (fromAbs fp2))
|
||||
(throwIO $ SameFile bs1 bs2)
|
||||
|
||||
|
||||
-- |Check if the files are the same by examining device and file id.
|
||||
-- This follows symbolic links.
|
||||
sameFile :: Path Abs -> Path Abs -> IO Bool
|
||||
sameFile fp1 fp2 =
|
||||
withAbsPath fp1 $ \fp1' -> withAbsPath fp2 $ \fp2' ->
|
||||
handleIOError (\_ -> return False) $ do
|
||||
fs1 <- getFileStatus fp1'
|
||||
fs2 <- getFileStatus fp2'
|
||||
sameFile :: Path b1 -> Path b2 -> IO Bool
|
||||
sameFile (MkPath fp1) (MkPath fp2) =
|
||||
handleIOError (\_ -> return False) $ do
|
||||
fs1 <- getFileStatus fp1
|
||||
fs2 <- getFileStatus fp2
|
||||
|
||||
if ((PF.deviceID fs1, PF.fileID fs1) ==
|
||||
(PF.deviceID fs2, PF.fileID fs2))
|
||||
then return True
|
||||
else return False
|
||||
if ((PF.deviceID fs1, PF.fileID fs1) ==
|
||||
(PF.deviceID fs2, PF.fileID fs2))
|
||||
then return True
|
||||
else return False
|
||||
|
||||
|
||||
-- TODO: make this more robust when destination does not exist
|
||||
@@ -218,66 +225,59 @@ sameFile fp1 fp2 =
|
||||
-- within the source directory by comparing the device+file ID of the
|
||||
-- source directory with all device+file IDs of the parent directories
|
||||
-- of the destination.
|
||||
throwDestinationInSource :: Path Abs -- ^ source dir
|
||||
-> Path Abs -- ^ full destination, @dirname dest@
|
||||
-- must exist
|
||||
throwDestinationInSource :: Path b1 -- ^ source dir
|
||||
-> Path b2 -- ^ full destination, @dirname dest@
|
||||
-- must exist
|
||||
-> IO ()
|
||||
throwDestinationInSource source dest = do
|
||||
throwDestinationInSource (MkPath sbs) dest@(MkPath dbs) = do
|
||||
destAbs <- toAbs dest
|
||||
dest' <- (\x -> maybe x (\y -> x </> y) $ basename dest)
|
||||
<$> (canonicalizePath $ dirname dest)
|
||||
<$> (canonicalizePath $ dirname destAbs)
|
||||
dids <- forM (getAllParents dest') $ \p -> do
|
||||
fs <- PF.getSymbolicLinkStatus (fromAbs p)
|
||||
return (PF.deviceID fs, PF.fileID fs)
|
||||
sid <- fmap (\x -> (PF.deviceID x, PF.fileID x))
|
||||
$ PF.getFileStatus (fromAbs source)
|
||||
$ PF.getFileStatus sbs
|
||||
when (elem sid dids)
|
||||
(throwIO $ DestinationInSource (fromAbs dest)
|
||||
(fromAbs source))
|
||||
(throwIO $ DestinationInSource dbs sbs)
|
||||
|
||||
|
||||
-- |Checks if the given file exists and is not a directory.
|
||||
-- Does not follow symlinks.
|
||||
doesFileExist :: Path Abs -> IO Bool
|
||||
doesFileExist fp =
|
||||
doesFileExist :: Path b -> IO Bool
|
||||
doesFileExist (MkPath bs) =
|
||||
handleIOError (\_ -> return False) $ do
|
||||
fs <- PF.getSymbolicLinkStatus (fromAbs fp)
|
||||
fs <- PF.getSymbolicLinkStatus bs
|
||||
return $ not . PF.isDirectory $ fs
|
||||
|
||||
|
||||
-- |Checks if the given file exists and is a directory.
|
||||
-- Does not follow symlinks.
|
||||
doesDirectoryExist :: Path Abs -> IO Bool
|
||||
doesDirectoryExist fp =
|
||||
doesDirectoryExist :: Path b -> IO Bool
|
||||
doesDirectoryExist (MkPath bs) =
|
||||
handleIOError (\_ -> return False) $ do
|
||||
fs <- PF.getSymbolicLinkStatus (fromAbs fp)
|
||||
fs <- PF.getSymbolicLinkStatus bs
|
||||
return $ PF.isDirectory fs
|
||||
|
||||
|
||||
-- |Checks whether a file or folder is writable.
|
||||
isWritable :: Path Abs -> IO Bool
|
||||
isWritable fp =
|
||||
isWritable :: Path b -> IO Bool
|
||||
isWritable (MkPath bs) =
|
||||
handleIOError (\_ -> return False) $
|
||||
fileAccess (fromAbs fp) False True False
|
||||
fileAccess bs False True False
|
||||
|
||||
|
||||
-- |Checks whether the directory at the given path exists and can be
|
||||
-- opened. This invokes `openDirStream` which follows symlinks.
|
||||
canOpenDirectory :: Path Abs -> IO Bool
|
||||
canOpenDirectory fp =
|
||||
canOpenDirectory :: Path b -> IO Bool
|
||||
canOpenDirectory (MkPath bs) =
|
||||
handleIOError (\_ -> return False) $ do
|
||||
bracket (PFD.openDirStream . fromAbs $ fp)
|
||||
bracket (PFD.openDirStream bs)
|
||||
PFD.closeDirStream
|
||||
(\_ -> return ())
|
||||
return True
|
||||
|
||||
|
||||
-- |Throws a `Can'tOpenDirectory` HPathIOException if the directory at the given
|
||||
-- path cannot be opened.
|
||||
throwCantOpenDirectory :: Path Abs -> IO ()
|
||||
throwCantOpenDirectory fp =
|
||||
unlessM (canOpenDirectory fp)
|
||||
(throwIO . Can'tOpenDirectory . fromAbs $ fp)
|
||||
|
||||
|
||||
|
||||
--------------------------------
|
||||
@@ -357,3 +357,4 @@ reactOnError a ios fmios =
|
||||
else y)
|
||||
(throwIO ex)
|
||||
fmios
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
-- |
|
||||
-- Module : HPath.IO.Utils
|
||||
-- Copyright : © 2016 Julian Ospald
|
||||
-- License : GPL-2
|
||||
--
|
||||
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
||||
-- Stability : experimental
|
||||
-- Portability : portable
|
||||
--
|
||||
-- Random and general IO/monad utilities.
|
||||
|
||||
|
||||
module HPath.IO.Utils where
|
||||
|
||||
|
||||
import Control.Monad
|
||||
(
|
||||
when
|
||||
, unless
|
||||
)
|
||||
|
||||
|
||||
-- |If the value of the first argument is True, then execute the action
|
||||
-- provided in the second argument, otherwise do nothing.
|
||||
whenM :: Monad m => m Bool -> m () -> m ()
|
||||
whenM mb a = mb >>= (`when` a)
|
||||
|
||||
|
||||
-- |If the value of the first argument is False, then execute the action
|
||||
-- provided in the second argument, otherwise do nothing.
|
||||
unlessM :: Monad m => m Bool -> m () -> m ()
|
||||
unlessM mb a = mb >>= (`unless` a)
|
||||
@@ -10,6 +10,7 @@
|
||||
-- Traversal and read operations on directories.
|
||||
|
||||
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
@@ -37,7 +38,10 @@ module System.Posix.Directory.Traversals (
|
||||
, realpath
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
|
||||
#if __GLASGOW_HASKELL__ < 710
|
||||
import Control.Applicative ((<$>))
|
||||
#endif
|
||||
import Control.Monad
|
||||
import System.Posix.FilePath ((</>))
|
||||
import System.Posix.Directory.Foreign
|
||||
|
||||
7
stack.yaml
Normal file
7
stack.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
resolver: lts-12.1
|
||||
|
||||
packages:
|
||||
- '.'
|
||||
|
||||
extra-deps:
|
||||
- IfElse-0.85
|
||||
108
test/HPath/IO/AppendFileSpec.hs
Normal file
108
test/HPath/IO/AppendFileSpec.hs
Normal file
@@ -0,0 +1,108 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
|
||||
module HPath.IO.AppendFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "AppendFileSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "fileWithContent"
|
||||
createRegularFile' "fileWithoutContent"
|
||||
createSymlink' "inputFileSymL" "fileWithContent"
|
||||
createDir' "alreadyExistsD"
|
||||
createRegularFile' "noPerms"
|
||||
noPerms "noPerms"
|
||||
createDir' "noPermsD"
|
||||
createRegularFile' "noPermsD/inputFile"
|
||||
noPerms "noPermsD"
|
||||
writeFile' "fileWithContent" "BLKASL"
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
deleteFile' "fileWithContent"
|
||||
deleteFile' "fileWithoutContent"
|
||||
deleteFile' "inputFileSymL"
|
||||
deleteDir' "alreadyExistsD"
|
||||
normalFilePerms "noPerms"
|
||||
deleteFile' "noPerms"
|
||||
normalDirPerms "noPermsD"
|
||||
deleteFile' "noPermsD/inputFile"
|
||||
deleteDir' "noPermsD"
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.appendFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "appendFile file with content, everything clear" $ do
|
||||
appendFile' "fileWithContent" "blahfaselllll"
|
||||
out <- readFile' "fileWithContent"
|
||||
out `shouldBe` "BLKASLblahfaselllll"
|
||||
|
||||
it "appendFile file with content, everything clear" $ do
|
||||
appendFile' "fileWithContent" "gagagaga"
|
||||
out <- readFile' "fileWithContent"
|
||||
out `shouldBe` "BLKASLblahfaselllllgagagaga"
|
||||
|
||||
it "appendFile file with content, everything clear" $ do
|
||||
appendFile' "fileWithContent" ""
|
||||
out <- readFile' "fileWithContent"
|
||||
out `shouldBe` "BLKASLblahfaselllllgagagaga"
|
||||
|
||||
it "appendFile file without content, everything clear" $ do
|
||||
appendFile' "fileWithoutContent" "blahfaselllll"
|
||||
out <- readFile' "fileWithoutContent"
|
||||
out `shouldBe` "blahfaselllll"
|
||||
|
||||
it "appendFile, everything clear" $ do
|
||||
appendFile' "fileWithoutContent" "gagagaga"
|
||||
out <- readFile' "fileWithoutContent"
|
||||
out `shouldBe` "blahfaselllllgagagaga"
|
||||
|
||||
it "appendFile symlink, everything clear" $ do
|
||||
appendFile' "inputFileSymL" "blahfaselllll"
|
||||
out <- readFile' "inputFileSymL"
|
||||
out `shouldBe` "BLKASLblahfaselllllgagagagablahfaselllll"
|
||||
|
||||
it "appendFile symlink, everything clear" $ do
|
||||
appendFile' "inputFileSymL" "gagagaga"
|
||||
out <- readFile' "inputFileSymL"
|
||||
out `shouldBe` "BLKASLblahfaselllllgagagagablahfaselllllgagagaga"
|
||||
|
||||
|
||||
-- posix failures --
|
||||
it "appendFile to dir, inappropriate type" $ do
|
||||
appendFile' "alreadyExistsD" ""
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "appendFile, no permissions to file" $ do
|
||||
appendFile' "noPerms" ""
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "appendFile, no permissions to file" $ do
|
||||
appendFile' "noPermsD/inputFile" ""
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "appendFile, file does not exist" $ do
|
||||
appendFile' "gaga" ""
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
||||
@@ -13,12 +13,15 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CanonicalizePathSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "file"
|
||||
@@ -37,7 +40,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.canonicalizePath" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
247
test/HPath/IO/CopyDirRecursiveCollectFailuresSpec.hs
Normal file
247
test/HPath/IO/CopyDirRecursiveCollectFailuresSpec.hs
Normal file
@@ -0,0 +1,247 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
|
||||
module HPath.IO.CopyDirRecursiveCollectFailuresSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import Data.List (sort)
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyDirRecursiveCollectFailuresSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "alreadyExists"
|
||||
createRegularFile' "wrongInput"
|
||||
createSymlink' "wrongInputSymL" "inputDir/"
|
||||
createDir' "alreadyExistsD"
|
||||
createDir' "noPerms"
|
||||
createDir' "noWritePerm"
|
||||
|
||||
createDir' "inputDir"
|
||||
createDir' "inputDir/bar"
|
||||
createDir' "inputDir/foo"
|
||||
createRegularFile' "inputDir/foo/inputFile1"
|
||||
createRegularFile' "inputDir/inputFile2"
|
||||
createRegularFile' "inputDir/bar/inputFile3"
|
||||
writeFile' "inputDir/foo/inputFile1" "SDAADSdsada"
|
||||
writeFile' "inputDir/inputFile2" "Blahfaselgagaga"
|
||||
writeFile' "inputDir/bar/inputFile3"
|
||||
"fdfdssdffsd3223sasdasdasdadasasddasdasdasasd4"
|
||||
|
||||
createDir' "inputDir1"
|
||||
createDir' "inputDir1/foo2"
|
||||
createDir' "inputDir1/foo2/foo3"
|
||||
createDir' "inputDir1/foo2/foo4"
|
||||
createRegularFile' "inputDir1/foo2/inputFile1"
|
||||
createRegularFile' "inputDir1/foo2/inputFile2"
|
||||
createRegularFile' "inputDir1/foo2/inputFile3"
|
||||
createRegularFile' "inputDir1/foo2/foo4/inputFile4"
|
||||
createRegularFile' "inputDir1/foo2/foo4/inputFile6"
|
||||
createRegularFile' "inputDir1/foo2/foo3/inputFile5"
|
||||
noPerms "inputDir1/foo2/foo3"
|
||||
|
||||
createDir' "outputDir1"
|
||||
createDir' "outputDir1/foo2"
|
||||
createDir' "outputDir1/foo2/foo4"
|
||||
createDir' "outputDir1/foo2/foo4/inputFile4"
|
||||
createRegularFile' "outputDir1/foo2/foo4/inputFile6"
|
||||
noPerms "outputDir1/foo2/foo4/inputFile4"
|
||||
noPerms "outputDir1/foo2/foo4"
|
||||
|
||||
noPerms "noPerms"
|
||||
noWritableDirPerms "noWritePerm"
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
normalDirPerms "noPerms"
|
||||
normalDirPerms "noWritePerm"
|
||||
|
||||
normalDirPerms "inputDir1/foo2/foo3"
|
||||
deleteFile' "inputDir1/foo2/foo4/inputFile4"
|
||||
deleteFile' "inputDir1/foo2/foo4/inputFile6"
|
||||
deleteFile' "inputDir1/foo2/inputFile1"
|
||||
deleteFile' "inputDir1/foo2/inputFile2"
|
||||
deleteFile' "inputDir1/foo2/inputFile3"
|
||||
deleteFile' "inputDir1/foo2/foo3/inputFile5"
|
||||
deleteDir' "inputDir1/foo2/foo3"
|
||||
deleteDir' "inputDir1/foo2/foo4"
|
||||
deleteDir' "inputDir1/foo2"
|
||||
deleteDir' "inputDir1"
|
||||
|
||||
normalDirPerms "outputDir1/foo2/foo4"
|
||||
normalDirPerms "outputDir1/foo2/foo4/inputFile4"
|
||||
deleteFile' "outputDir1/foo2/foo4/inputFile6"
|
||||
deleteDir' "outputDir1/foo2/foo4/inputFile4"
|
||||
deleteDir' "outputDir1/foo2/foo4"
|
||||
deleteDir' "outputDir1/foo2"
|
||||
deleteDir' "outputDir1"
|
||||
|
||||
deleteFile' "alreadyExists"
|
||||
deleteFile' "wrongInput"
|
||||
deleteFile' "wrongInputSymL"
|
||||
deleteDir' "alreadyExistsD"
|
||||
deleteDir' "noPerms"
|
||||
deleteDir' "noWritePerm"
|
||||
deleteFile' "inputDir/foo/inputFile1"
|
||||
deleteFile' "inputDir/inputFile2"
|
||||
deleteFile' "inputDir/bar/inputFile3"
|
||||
deleteDir' "inputDir/foo"
|
||||
deleteDir' "inputDir/bar"
|
||||
deleteDir' "inputDir"
|
||||
|
||||
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.copyDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyDirRecursive (Strict, CollectFailures), all fine and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "outputDir")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
-- posix failures --
|
||||
it "copyDirRecursive (Strict, CollectFailures), source directory does not exist" $
|
||||
copyDirRecursive' "doesNotExist"
|
||||
"outputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), cannot open source dir" $
|
||||
copyDirRecursive' "noPerms/inputDir"
|
||||
"foo"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
|
||||
-- custom failures
|
||||
it "copyDirRecursive (Overwrite, CollectFailures), various failures" $ do
|
||||
copyDirRecursive' "inputDir1/foo2"
|
||||
"outputDir1/foo2"
|
||||
Overwrite
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure ex@[_, _]) ->
|
||||
any (\(h, e) -> ioeGetErrorType e == InappropriateType
|
||||
&& isCopyFileFailed h) ex &&
|
||||
any (\(h, e) -> ioeGetErrorType e == PermissionDenied
|
||||
&& isReadContentsFailed h) ex)
|
||||
normalDirPerms "outputDir1/foo2/foo4"
|
||||
normalDirPerms "outputDir1/foo2/foo4/inputFile4"
|
||||
c <- allDirectoryContents' "outputDir1"
|
||||
tmpDir' <- getRawTmpDir
|
||||
let shouldC = (fmap (\x -> tmpDir' `BS.append` x)
|
||||
["outputDir1"
|
||||
,"outputDir1/foo2"
|
||||
,"outputDir1/foo2/inputFile1"
|
||||
,"outputDir1/foo2/inputFile2"
|
||||
,"outputDir1/foo2/inputFile3"
|
||||
,"outputDir1/foo2/foo4"
|
||||
,"outputDir1/foo2/foo4/inputFile6"
|
||||
,"outputDir1/foo2/foo4/inputFile4"])
|
||||
deleteFile' "outputDir1/foo2/inputFile1"
|
||||
deleteFile' "outputDir1/foo2/inputFile2"
|
||||
deleteFile' "outputDir1/foo2/inputFile3"
|
||||
sort c `shouldBe` sort shouldC
|
||||
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), no write permission on output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noWritePerm/foo"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure [(CreateDirFailed{}, e)]) -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), cannot open output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noPerms/foo"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
isRecursiveFailure
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), destination dir already exists" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure [(CreateDirFailed{}, e)]) -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), destination already exists and is a file" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
isRecursiveFailure
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), wrong input (regular file)" $
|
||||
copyDirRecursive' "wrongInput"
|
||||
"outputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure [(ReadContentsFailed{}, e)]) -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), wrong input (symlink to directory)" $
|
||||
copyDirRecursive' "wrongInputSymL"
|
||||
"outputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure [(ReadContentsFailed{}, e)]) -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), destination in source" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir/foo"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
isDestinationInSource
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), destination and source same directory" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ module HPath.IO.CopyDirRecursiveOverwriteSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -17,11 +18,16 @@ import GHC.IO.Exception
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyDirRecursiveOverwriteSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "alreadyExists"
|
||||
@@ -81,89 +87,116 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.copyDirRecursiveOverwrite" $ do
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.copyDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyDirRecursiveOverwrite, all fine" $ do
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"outputDir"
|
||||
it "copyDirRecursive (Overwrite, FailEarly), all fine" $ do
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
it "copyDirRecursiveOverwrite, all fine and compare" $ do
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"outputDir"
|
||||
it "copyDirRecursive (Overwrite, FailEarly), all fine and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir ++ "inputDir" ++ " "
|
||||
++ toString tmpDir ++ "outputDir")
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "outputDir")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
it "copyDirRecursiveOverwrite, destination dir already exists" $ do
|
||||
it "copyDirRecursive (Overwrite, FailEarly), destination dir already exists" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir ++ "inputDir" ++ " "
|
||||
++ toString tmpDir ++ "alreadyExistsD")
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "alreadyExistsD")
|
||||
`shouldReturn` (ExitFailure 1)
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"alreadyExistsD"
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExistsD"
|
||||
Overwrite
|
||||
FailEarly
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir ++ "inputDir" ++ " "
|
||||
++ toString tmpDir ++ "alreadyExistsD")
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "alreadyExistsD")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
|
||||
-- posix failures --
|
||||
it "copyDirRecursiveOverwrite, source directory does not exist" $
|
||||
copyDirRecursiveOverwrite' "doesNotExist"
|
||||
"outputDir"
|
||||
it "copyDirRecursive, source directory does not exist" $
|
||||
copyDirRecursive' "doesNotExist"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyDirRecursiveOverwrite, no write permission on output dir" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"noWritePerm/foo"
|
||||
it "copyDirRecursive, no write permission on output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noWritePerm/foo"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursiveOverwrite, cannot open output dir" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"noPerms/foo"
|
||||
it "copyDirRecursive, cannot open output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noPerms/foo"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursiveOverwrite, cannot open source dir" $
|
||||
copyDirRecursiveOverwrite' "noPerms/inputDir"
|
||||
"foo"
|
||||
it "copyDirRecursive, cannot open source dir" $
|
||||
copyDirRecursive' "noPerms/inputDir"
|
||||
"foo"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursiveOverwrite, destination already exists and is a file" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"alreadyExists"
|
||||
it "copyDirRecursive, destination already exists and is a file" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExists"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyDirRecursiveOverwrite, wrong input (regular file)" $
|
||||
copyDirRecursiveOverwrite' "wrongInput"
|
||||
"outputDir"
|
||||
it "copyDirRecursive, wrong input (regular file)" $
|
||||
copyDirRecursive' "wrongInput"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyDirRecursiveOverwrite, wrong input (symlink to directory)" $
|
||||
copyDirRecursiveOverwrite' "wrongInputSymL"
|
||||
"outputDir"
|
||||
it "copyDirRecursive, wrong input (symlink to directory)" $
|
||||
copyDirRecursive' "wrongInputSymL"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
-- custom failures
|
||||
it "copyDirRecursiveOverwrite, destination in source" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"inputDir/foo"
|
||||
it "copyDirRecursive (Overwrite, FailEarly), destination in source" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir/foo"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
isDestinationInSource
|
||||
|
||||
it "copyDirRecursiveOverwrite, destination and source same directory" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"inputDir"
|
||||
it "copyDirRecursive (Overwrite, FailEarly), destination and source same directory" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
@@ -5,6 +5,7 @@ module HPath.IO.CopyDirRecursiveSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -17,12 +18,14 @@ import GHC.IO.Exception
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyDirRecursiveSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
@@ -69,82 +72,109 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.copyDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyDirRecursive, all fine" $ do
|
||||
it "copyDirRecursive (Strict, FailEarly), all fine" $ do
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
it "copyDirRecursive, all fine and compare" $ do
|
||||
it "copyDirRecursive (Strict, FailEarly), all fine and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir ++ "inputDir" ++ " "
|
||||
++ toString tmpDir ++ "outputDir")
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "outputDir")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
-- posix failures --
|
||||
it "copyDirRecursive, source directory does not exist" $
|
||||
it "copyDirRecursive (Strict, FailEarly), source directory does not exist" $
|
||||
copyDirRecursive' "doesNotExist"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyDirRecursive, no write permission on output dir" $
|
||||
it "copyDirRecursive (Strict, FailEarly), no write permission on output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noWritePerm/foo"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive, cannot open output dir" $
|
||||
it "copyDirRecursive (Strict, FailEarly), cannot open output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noPerms/foo"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive, cannot open source dir" $
|
||||
it "copyDirRecursive (Strict, FailEarly), cannot open source dir" $
|
||||
copyDirRecursive' "noPerms/inputDir"
|
||||
"foo"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive, destination dir already exists" $
|
||||
it "copyDirRecursive (Strict, FailEarly), destination dir already exists" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "copyDirRecursive, destination already exists and is a file" $
|
||||
it "copyDirRecursive (Strict, FailEarly), destination already exists and is a file" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "copyDirRecursive, wrong input (regular file)" $
|
||||
it "copyDirRecursive (Strict, FailEarly), wrong input (regular file)" $
|
||||
copyDirRecursive' "wrongInput"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyDirRecursive, wrong input (symlink to directory)" $
|
||||
it "copyDirRecursive (Strict, FailEarly), wrong input (symlink to directory)" $
|
||||
copyDirRecursive' "wrongInputSymL"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
-- custom failures
|
||||
it "copyDirRecursive, destination in source" $
|
||||
it "copyDirRecursive (Strict, FailEarly), destination in source" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir/foo"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
isDestinationInSource
|
||||
|
||||
it "copyDirRecursive, destination and source same directory" $
|
||||
it "copyDirRecursive (Strict, FailEarly), destination and source same directory" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ module HPath.IO.CopyFileOverwriteSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -16,8 +17,14 @@ import GHC.IO.Exception
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyFileOverwriteSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -51,79 +58,91 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.copyFileOverwrite" $ do
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.copyFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyFileOverwrite, everything clear" $ do
|
||||
copyFileOverwrite' "inputFile"
|
||||
it "copyFile (Overwrite), everything clear" $ do
|
||||
copyFile' "inputFile"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
removeFileIfExists "outputFile"
|
||||
|
||||
it "copyFileOverwrite, output file already exists, all clear" $ do
|
||||
copyFile' "alreadyExists" "alreadyExists.bak"
|
||||
copyFileOverwrite' "inputFile"
|
||||
"alreadyExists"
|
||||
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " "
|
||||
++ toString tmpDir ++ "alreadyExists")
|
||||
it "copyFile (Overwrite), output file already exists, all clear" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyFile' "alreadyExists" "alreadyExists.bak" Strict
|
||||
copyFile' "inputFile" "alreadyExists" Overwrite
|
||||
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||
++ toString tmpDir' ++ "alreadyExists")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeFileIfExists "alreadyExists"
|
||||
copyFile' "alreadyExists.bak" "alreadyExists"
|
||||
copyFile' "alreadyExists.bak" "alreadyExists" Strict
|
||||
removeFileIfExists "alreadyExists.bak"
|
||||
|
||||
it "copyFileOverwrite, and compare" $ do
|
||||
copyFileOverwrite' "inputFile"
|
||||
it "copyFile (Overwrite), and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyFile' "inputFile"
|
||||
"outputFile"
|
||||
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " "
|
||||
++ toString tmpDir ++ "outputFile")
|
||||
Overwrite
|
||||
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||
++ toString tmpDir' ++ "outputFile")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeFileIfExists "outputFile"
|
||||
|
||||
|
||||
-- posix failures --
|
||||
it "copyFileOverwrite, input file does not exist" $
|
||||
copyFileOverwrite' "noSuchFile"
|
||||
it "copyFile (Overwrite), input file does not exist" $
|
||||
copyFile' "noSuchFile"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyFileOverwrite, no permission to write to output directory" $
|
||||
copyFileOverwrite' "inputFile"
|
||||
it "copyFile (Overwrite), no permission to write to output directory" $
|
||||
copyFile' "inputFile"
|
||||
"outputDirNoWrite/outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFileOverwrite, cannot open output directory" $
|
||||
copyFileOverwrite' "inputFile"
|
||||
it "copyFile (Overwrite), cannot open output directory" $
|
||||
copyFile' "inputFile"
|
||||
"noPerms/outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFileOverwrite, cannot open source directory" $
|
||||
copyFileOverwrite' "noPerms/inputFile"
|
||||
it "copyFile (Overwrite), cannot open source directory" $
|
||||
copyFile' "noPerms/inputFile"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFileOverwrite, wrong input type (symlink)" $
|
||||
copyFileOverwrite' "inputFileSymL"
|
||||
it "copyFile (Overwrite), wrong input type (symlink)" $
|
||||
copyFile' "inputFileSymL"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "copyFileOverwrite, wrong input type (directory)" $
|
||||
copyFileOverwrite' "wrongInput"
|
||||
it "copyFile (Overwrite), wrong input type (directory)" $
|
||||
copyFile' "wrongInput"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyFileOverwrite, output file already exists and is a dir" $
|
||||
copyFileOverwrite' "inputFile"
|
||||
it "copyFile (Overwrite), output file already exists and is a dir" $
|
||||
copyFile' "inputFile"
|
||||
"alreadyExistsD"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
-- custom failures --
|
||||
it "copyFileOverwrite, output and input are same file" $
|
||||
copyFileOverwrite' "inputFile"
|
||||
it "copyFile (Overwrite), output and input are same file" $
|
||||
copyFile' "inputFile"
|
||||
"inputFile"
|
||||
Overwrite
|
||||
`shouldThrow` isSameFile
|
||||
|
||||
@@ -5,6 +5,7 @@ module HPath.IO.CopyFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -17,10 +18,15 @@ import GHC.IO.Exception
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyFileSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "inputFile"
|
||||
@@ -51,75 +57,87 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.copyFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyFile, everything clear" $ do
|
||||
it "copyFile (Strict), everything clear" $ do
|
||||
copyFile' "inputFile"
|
||||
"outputFile"
|
||||
Strict
|
||||
removeFileIfExists "outputFile"
|
||||
|
||||
it "copyFile, and compare" $ do
|
||||
it "copyFile (Strict), and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyFile' "inputFile"
|
||||
"outputFile"
|
||||
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " "
|
||||
++ toString tmpDir ++ "outputFile")
|
||||
Strict
|
||||
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||
++ toString tmpDir' ++ "outputFile")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeFileIfExists "outputFile"
|
||||
|
||||
-- posix failures --
|
||||
it "copyFile, input file does not exist" $
|
||||
it "copyFile (Strict), input file does not exist" $
|
||||
copyFile' "noSuchFile"
|
||||
"outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyFile, no permission to write to output directory" $
|
||||
it "copyFile (Strict), no permission to write to output directory" $
|
||||
copyFile' "inputFile"
|
||||
"outputDirNoWrite/outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile, cannot open output directory" $
|
||||
it "copyFile (Strict), cannot open output directory" $
|
||||
copyFile' "inputFile"
|
||||
"noPerms/outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile, cannot open source directory" $
|
||||
it "copyFile (Strict), cannot open source directory" $
|
||||
copyFile' "noPerms/inputFile"
|
||||
"outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile, wrong input type (symlink)" $
|
||||
it "copyFile (Strict), wrong input type (symlink)" $
|
||||
copyFile' "inputFileSymL"
|
||||
"outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "copyFile, wrong input type (directory)" $
|
||||
it "copyFile (Strict), wrong input type (directory)" $
|
||||
copyFile' "wrongInput"
|
||||
"outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyFile, output file already exists" $
|
||||
it "copyFile (Strict), output file already exists" $
|
||||
copyFile' "inputFile"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "copyFile, output file already exists and is a dir" $
|
||||
it "copyFile (Strict), output file already exists and is a dir" $
|
||||
copyFile' "inputFile"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
-- custom failures --
|
||||
it "copyFile, output and input are same file" $
|
||||
it "copyFile (Strict), output and input are same file" $
|
||||
copyFile' "inputFile"
|
||||
"inputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
78
test/HPath/IO/CreateDirRecursiveSpec.hs
Normal file
78
test/HPath/IO/CreateDirRecursiveSpec.hs
Normal file
@@ -0,0 +1,78 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module HPath.IO.CreateDirRecursiveSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CreateDirRecursiveSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createDir' "alreadyExists"
|
||||
createRegularFile' "alreadyExistsF"
|
||||
createDir' "noPerms"
|
||||
createDir' "noWritePerms"
|
||||
noPerms "noPerms"
|
||||
noWritableDirPerms "noWritePerms"
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
normalDirPerms "noPerms"
|
||||
normalDirPerms "noWritePerms"
|
||||
deleteDir' "alreadyExists"
|
||||
deleteDir' "noPerms"
|
||||
deleteDir' "noWritePerms"
|
||||
deleteFile' "alreadyExistsF"
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.createDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
it "createDirRecursive, all fine" $ do
|
||||
createDirRecursive' "newDir"
|
||||
deleteDir' "newDir"
|
||||
|
||||
it "createDirRecursive, parent directories do not exist" $ do
|
||||
createDirRecursive' "some/thing/dada"
|
||||
deleteDir' "some/thing/dada"
|
||||
deleteDir' "some/thing"
|
||||
deleteDir' "some"
|
||||
|
||||
it "createDirRecursive, destination directory already exists" $
|
||||
createDirRecursive' "alreadyExists"
|
||||
|
||||
-- posix failures --
|
||||
it "createDirRecursive, destination already exists and is a file" $
|
||||
createDirRecursive' "alreadyExistsF"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "createDirRecursive, can't write to output directory" $
|
||||
createDirRecursive' "noWritePerms/newDir"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "createDirRecursive, can't open output directory" $
|
||||
createDirRecursive' "noPerms/newDir"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
|
||||
|
||||
@@ -13,10 +13,14 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CreateDirSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createDir' "alreadyExists"
|
||||
@@ -37,7 +41,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.createDir" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -46,6 +50,11 @@ spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
removeDirIfExists "newDir"
|
||||
|
||||
-- posix failures --
|
||||
it "createDir, parent directories do not exist" $
|
||||
createDir' "some/thing/dada"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "createDir, can't write to output directory" $
|
||||
createDir' "noWritePerms/newDir"
|
||||
`shouldThrow`
|
||||
|
||||
@@ -13,10 +13,14 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CreateRegularFileSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "alreadyExists"
|
||||
@@ -25,8 +29,6 @@ setupFiles = do
|
||||
noPerms "noPerms"
|
||||
noWritableDirPerms "noWritePerms"
|
||||
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
normalDirPerms "noPerms"
|
||||
@@ -37,7 +39,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.createRegularFile" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -46,6 +48,11 @@ spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
removeFileIfExists "newDir"
|
||||
|
||||
-- posix failures --
|
||||
it "createRegularFile, parent directories do not exist" $
|
||||
createRegularFile' "some/thing/dada"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "createRegularFile, can't write to destination directory" $
|
||||
createRegularFile' "noWritePerms/newDir"
|
||||
`shouldThrow`
|
||||
|
||||
@@ -4,7 +4,6 @@ module HPath.IO.CreateSymlinkSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
@@ -14,8 +13,12 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CreateSymlinkSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -37,7 +40,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.createSymlink" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -46,6 +49,11 @@ spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
removeFileIfExists "newSymL"
|
||||
|
||||
-- posix failures --
|
||||
it "createSymlink, parent directories do not exist" $
|
||||
createSymlink' "some/thing/dada" "lala"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "createSymlink, can't write to destination directory" $
|
||||
createSymlink' "noWritePerms/newDir" "lala"
|
||||
`shouldThrow`
|
||||
|
||||
@@ -17,8 +17,13 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "DeleteDirRecursiveSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -46,7 +51,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.deleteDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -17,8 +17,14 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "DeleteDirSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -46,7 +52,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.deleteDir" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -4,6 +4,7 @@ module HPath.IO.DeleteFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
@@ -17,8 +18,12 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "DeleteFileSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -41,7 +46,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.deleteFile" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -55,6 +60,7 @@ spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
it "deleteFile, symlink, all fine" $ do
|
||||
recreateSymlink' "syml"
|
||||
"testFile"
|
||||
Strict
|
||||
deleteFile' "testFile"
|
||||
getSymbolicLinkStatus "testFile"
|
||||
`shouldThrow`
|
||||
|
||||
@@ -3,18 +3,10 @@
|
||||
module HPath.IO.GetDirsFilesSpec where
|
||||
|
||||
|
||||
import Control.Applicative
|
||||
(
|
||||
(<$>)
|
||||
)
|
||||
import Data.List
|
||||
(
|
||||
sort
|
||||
)
|
||||
import Data.Maybe
|
||||
(
|
||||
fromJust
|
||||
)
|
||||
import qualified HPath as P
|
||||
import HPath.IO
|
||||
import Test.Hspec
|
||||
@@ -22,17 +14,17 @@ import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import System.Posix.Env.ByteString
|
||||
(
|
||||
getEnv
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "GetDirsFilesSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -61,7 +53,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.getDirsFiles" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -14,8 +14,13 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "GetFileTypeSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -42,7 +47,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.getFileType" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -4,6 +4,7 @@ module HPath.IO.MoveFileOverwriteSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -14,8 +15,13 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "MoveFileOverwriteSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -45,65 +51,76 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.moveFileOverwrite" $ do
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.moveFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "moveFileOverwrite, all fine" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"movedFile"
|
||||
it "moveFile (Overwrite), all fine" $
|
||||
moveFile' "myFile"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
|
||||
it "moveFileOverwrite, all fine" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"dir/movedFile"
|
||||
it "moveFile (Overwrite), all fine" $
|
||||
moveFile' "myFile"
|
||||
"dir/movedFile"
|
||||
Overwrite
|
||||
|
||||
it "moveFileOverwrite, all fine on symlink" $
|
||||
moveFileOverwrite' "myFileL"
|
||||
"movedFile"
|
||||
it "moveFile (Overwrite), all fine on symlink" $
|
||||
moveFile' "myFileL"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
|
||||
it "moveFileOverwrite, all fine on directory" $
|
||||
moveFileOverwrite' "dir"
|
||||
"movedFile"
|
||||
it "moveFile (Overwrite), all fine on directory" $
|
||||
moveFile' "dir"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
|
||||
it "moveFileOverwrite, destination file already exists" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"alreadyExists"
|
||||
it "moveFile (Overwrite), destination file already exists" $
|
||||
moveFile' "myFile"
|
||||
"alreadyExists"
|
||||
Overwrite
|
||||
|
||||
-- posix failures --
|
||||
it "moveFileOverwrite, source file does not exist" $
|
||||
moveFileOverwrite' "fileDoesNotExist"
|
||||
"movedFile"
|
||||
it "moveFile (Overwrite), source file does not exist" $
|
||||
moveFile' "fileDoesNotExist"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "moveFileOverwrite, can't write to destination directory" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"noWritePerm/movedFile"
|
||||
it "moveFile (Overwrite), can't write to destination directory" $
|
||||
moveFile' "myFile"
|
||||
"noWritePerm/movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "moveFileOverwrite, can't open destination directory" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"noPerms/movedFile"
|
||||
it "moveFile (Overwrite), can't open destination directory" $
|
||||
moveFile' "myFile"
|
||||
"noPerms/movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "moveFileOverwrite, can't open source directory" $
|
||||
moveFileOverwrite' "noPerms/myFile"
|
||||
"movedFile"
|
||||
it "moveFile (Overwrite), can't open source directory" $
|
||||
moveFile' "noPerms/myFile"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
-- custom failures --
|
||||
it "moveFileOverwrite, move from file to dir" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"alreadyExistsD"
|
||||
`shouldThrow`
|
||||
isDirDoesExist
|
||||
|
||||
it "moveFileOverwrite, source and dest are same file" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"myFile"
|
||||
it "moveFile (Overwrite), move from file to dir" $
|
||||
moveFile' "myFile"
|
||||
"alreadyExistsD"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "moveFile (Overwrite), source and dest are same file" $
|
||||
moveFile' "myFile"
|
||||
"myFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ module HPath.IO.MoveFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -14,8 +15,13 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "MoveFileSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -47,67 +53,77 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.moveFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "moveFile, all fine" $
|
||||
it "moveFile (Strict), all fine" $
|
||||
moveFile' "myFile"
|
||||
"movedFile"
|
||||
Strict
|
||||
|
||||
it "moveFile, all fine" $
|
||||
it "moveFile (Strict), all fine" $
|
||||
moveFile' "myFile"
|
||||
"dir/movedFile"
|
||||
Strict
|
||||
|
||||
it "moveFile, all fine on symlink" $
|
||||
it "moveFile (Strict), all fine on symlink" $
|
||||
moveFile' "myFileL"
|
||||
"movedFile"
|
||||
Strict
|
||||
|
||||
it "moveFile, all fine on directory" $
|
||||
it "moveFile (Strict), all fine on directory" $
|
||||
moveFile' "dir"
|
||||
"movedFile"
|
||||
Strict
|
||||
|
||||
-- posix failures --
|
||||
it "moveFile, source file does not exist" $
|
||||
it "moveFile (Strict), source file does not exist" $
|
||||
moveFile' "fileDoesNotExist"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "moveFile, can't write to destination directory" $
|
||||
it "moveFile (Strict), can't write to destination directory" $
|
||||
moveFile' "myFile"
|
||||
"noWritePerm/movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "moveFile, can't open destination directory" $
|
||||
it "moveFile (Strict), can't open destination directory" $
|
||||
moveFile' "myFile"
|
||||
"noPerms/movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "moveFile, can't open source directory" $
|
||||
it "moveFile (Strict), can't open source directory" $
|
||||
moveFile' "noPerms/myFile"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
-- custom failures --
|
||||
it "moveFile, destination file already exists" $
|
||||
it "moveFile (Strict), destination file already exists" $
|
||||
moveFile' "myFile"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
isFileDoesExist
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "moveFile, move from file to dir" $
|
||||
it "moveFile (Strict), move from file to dir" $
|
||||
moveFile' "myFile"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
isDirDoesExist
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "moveFile, source and dest are same file" $
|
||||
it "moveFile (Strict), source and dest are same file" $
|
||||
moveFile' "myFile"
|
||||
"myFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
85
test/HPath/IO/ReadFileEOFSpec.hs
Normal file
85
test/HPath/IO/ReadFileEOFSpec.hs
Normal file
@@ -0,0 +1,85 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
|
||||
module HPath.IO.ReadFileEOFSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "ReadFileEOFSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "fileWithContent"
|
||||
createRegularFile' "fileWithoutContent"
|
||||
createSymlink' "inputFileSymL" "fileWithContent"
|
||||
createDir' "alreadyExistsD"
|
||||
createRegularFile' "noPerms"
|
||||
noPerms "noPerms"
|
||||
createDir' "noPermsD"
|
||||
createRegularFile' "noPermsD/inputFile"
|
||||
noPerms "noPermsD"
|
||||
writeFile' "fileWithContent" "Blahfaselgagaga"
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
deleteFile' "fileWithContent"
|
||||
deleteFile' "fileWithoutContent"
|
||||
deleteFile' "inputFileSymL"
|
||||
deleteDir' "alreadyExistsD"
|
||||
normalFilePerms "noPerms"
|
||||
deleteFile' "noPerms"
|
||||
normalDirPerms "noPermsD"
|
||||
deleteFile' "noPermsD/inputFile"
|
||||
deleteDir' "noPermsD"
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.readFileEOF" $ do
|
||||
|
||||
-- successes --
|
||||
it "readFileEOF (Strict) file with content, everything clear" $ do
|
||||
out <- readFileEOF' "fileWithContent"
|
||||
out `shouldBe` "Blahfaselgagaga"
|
||||
|
||||
it "readFileEOF (Strict) symlink, everything clear" $ do
|
||||
out <- readFileEOF' "inputFileSymL"
|
||||
out `shouldBe` "Blahfaselgagaga"
|
||||
|
||||
it "readFileEOF (Strict) empty file, everything clear" $ do
|
||||
out <- readFileEOF' "fileWithoutContent"
|
||||
out `shouldBe` ""
|
||||
|
||||
|
||||
-- posix failures --
|
||||
it "readFileEOF (Strict) directory, wrong file type" $ do
|
||||
readFileEOF' "alreadyExistsD"
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "readFileEOF (Strict) file, no permissions" $ do
|
||||
readFileEOF' "noPerms"
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "readFileEOF (Strict) file, no permissions on dir" $ do
|
||||
readFileEOF' "noPermsD/inputFile"
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "readFileEOF (Strict) file, no such file" $ do
|
||||
readFileEOF' "lalala"
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
||||
85
test/HPath/IO/ReadFileSpec.hs
Normal file
85
test/HPath/IO/ReadFileSpec.hs
Normal file
@@ -0,0 +1,85 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
|
||||
module HPath.IO.ReadFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "ReadFileSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "fileWithContent"
|
||||
createRegularFile' "fileWithoutContent"
|
||||
createSymlink' "inputFileSymL" "fileWithContent"
|
||||
createDir' "alreadyExistsD"
|
||||
createRegularFile' "noPerms"
|
||||
noPerms "noPerms"
|
||||
createDir' "noPermsD"
|
||||
createRegularFile' "noPermsD/inputFile"
|
||||
noPerms "noPermsD"
|
||||
writeFile' "fileWithContent" "Blahfaselgagaga"
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
deleteFile' "fileWithContent"
|
||||
deleteFile' "fileWithoutContent"
|
||||
deleteFile' "inputFileSymL"
|
||||
deleteDir' "alreadyExistsD"
|
||||
normalFilePerms "noPerms"
|
||||
deleteFile' "noPerms"
|
||||
normalDirPerms "noPermsD"
|
||||
deleteFile' "noPermsD/inputFile"
|
||||
deleteDir' "noPermsD"
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.readFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "readFile (Strict) file with content, everything clear" $ do
|
||||
out <- readFile' "fileWithContent"
|
||||
out `shouldBe` "Blahfaselgagaga"
|
||||
|
||||
it "readFile (Strict) symlink, everything clear" $ do
|
||||
out <- readFile' "inputFileSymL"
|
||||
out `shouldBe` "Blahfaselgagaga"
|
||||
|
||||
it "readFile (Strict) empty file, everything clear" $ do
|
||||
out <- readFile' "fileWithoutContent"
|
||||
out `shouldBe` ""
|
||||
|
||||
|
||||
-- posix failures --
|
||||
it "readFile (Strict) directory, wrong file type" $ do
|
||||
readFile' "alreadyExistsD"
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "readFile (Strict) file, no permissions" $ do
|
||||
readFile' "noPerms"
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "readFile (Strict) file, no permissions on dir" $ do
|
||||
readFile' "noPermsD/inputFile"
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "readFile (Strict) file, no such file" $ do
|
||||
readFile' "lalala"
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
||||
139
test/HPath/IO/RecreateSymlinkOverwriteSpec.hs
Normal file
139
test/HPath/IO/RecreateSymlinkOverwriteSpec.hs
Normal file
@@ -0,0 +1,139 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module HPath.IO.RecreateSymlinkOverwriteSpec where
|
||||
|
||||
|
||||
-- TODO: exception if destination exists but is not a file + `OverWrite` CopyMode
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "RecreateSymlinkOverwriteSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "myFile"
|
||||
createSymlink' "myFileL" "myFile"
|
||||
createRegularFile' "alreadyExists"
|
||||
createDir' "alreadyExistsD"
|
||||
createDir' "dir"
|
||||
createDir' "noPerms"
|
||||
createDir' "noWritePerm"
|
||||
createDir' "alreadyExistsD2"
|
||||
createRegularFile' "alreadyExistsD2/lala"
|
||||
noPerms "noPerms"
|
||||
noWritableDirPerms "noWritePerm"
|
||||
writeFile' "myFile" "Blahfaselgagaga"
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
normalDirPerms "noPerms"
|
||||
normalDirPerms "noWritePerm"
|
||||
deleteFile' "myFile"
|
||||
deleteFile' "myFileL"
|
||||
deleteFile' "alreadyExists"
|
||||
deleteFile' "alreadyExistsD2/lala"
|
||||
deleteDir' "alreadyExistsD"
|
||||
deleteDir' "alreadyExistsD2"
|
||||
deleteDir' "dir"
|
||||
deleteDir' "noPerms"
|
||||
deleteDir' "noWritePerm"
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.recreateSymlink" $ do
|
||||
|
||||
-- successes --
|
||||
it "recreateSymLink (Overwrite), all fine" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
removeFileIfExists "movedFile"
|
||||
|
||||
it "recreateSymLink (Overwrite), all fine" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"dir/movedFile"
|
||||
Overwrite
|
||||
removeFileIfExists "dir/movedFile"
|
||||
|
||||
it "recreateSymLink (Overwrite), destination file already exists" $
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExists"
|
||||
Overwrite
|
||||
|
||||
it "recreateSymLink (Overwrite), destination already exists and is an empty dir" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExistsD"
|
||||
Overwrite
|
||||
deleteFile' "alreadyExistsD"
|
||||
createDir' "alreadyExistsD"
|
||||
|
||||
-- posix failures --
|
||||
it "recreateSymLink (Overwrite), destination already exists and is a non-empty dir" $
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExistsD2"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == UnsatisfiedConstraints)
|
||||
|
||||
it "recreateSymLink (Overwrite), wrong input type (file)" $
|
||||
recreateSymlink' "myFile"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "recreateSymLink (Overwrite), wrong input type (directory)" $
|
||||
recreateSymlink' "dir"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "recreateSymLink (Overwrite), can't write to destination directory" $
|
||||
recreateSymlink' "myFileL"
|
||||
"noWritePerm/movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink (Overwrite), can't open destination directory" $
|
||||
recreateSymlink' "myFileL"
|
||||
"noPerms/movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink (Overwrite), can't open source directory" $
|
||||
recreateSymlink' "noPerms/myFileL"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
-- custom failures --
|
||||
it "recreateSymLink (Overwrite), source and destination are the same file" $
|
||||
recreateSymlink' "myFileL"
|
||||
"myFileL"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
module HPath.IO.RecreateSymlinkSpec where
|
||||
|
||||
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -14,8 +17,13 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "RecreateSymlinkSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -46,67 +54,77 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.recreateSymlink" $ do
|
||||
|
||||
-- successes --
|
||||
it "recreateSymLink, all fine" $ do
|
||||
it "recreateSymLink (Strict), all fine" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"movedFile"
|
||||
Strict
|
||||
removeFileIfExists "movedFile"
|
||||
|
||||
it "recreateSymLink, all fine" $ do
|
||||
it "recreateSymLink (Strict), all fine" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"dir/movedFile"
|
||||
Strict
|
||||
removeFileIfExists "dir/movedFile"
|
||||
|
||||
-- posix failures --
|
||||
it "recreateSymLink, wrong input type (file)" $
|
||||
it "recreateSymLink (Strict), wrong input type (file)" $
|
||||
recreateSymlink' "myFile"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "recreateSymLink, wrong input type (directory)" $
|
||||
it "recreateSymLink (Strict), wrong input type (directory)" $
|
||||
recreateSymlink' "dir"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "recreateSymLink, can't write to destination directory" $
|
||||
it "recreateSymLink (Strict), can't write to destination directory" $
|
||||
recreateSymlink' "myFileL"
|
||||
"noWritePerm/movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink, can't open destination directory" $
|
||||
it "recreateSymLink (Strict), can't open destination directory" $
|
||||
recreateSymlink' "myFileL"
|
||||
"noPerms/movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink, can't open source directory" $
|
||||
it "recreateSymLink (Strict), can't open source directory" $
|
||||
recreateSymlink' "noPerms/myFileL"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink, destination file already exists" $
|
||||
it "recreateSymLink (Strict), destination file already exists" $
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "recreateSymLink, destination already exists and is a dir" $
|
||||
it "recreateSymLink (Strict), destination already exists and is a dir" $
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
-- custom failures --
|
||||
it "recreateSymLink, source and destination are the same file" $
|
||||
it "recreateSymLink (Strict), source and destination are the same file" $
|
||||
recreateSymlink' "myFileL"
|
||||
"myFileL"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
@@ -14,8 +14,13 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "RenameFileSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -46,7 +51,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.renameFile" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -96,13 +101,13 @@ spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
renameFile' "myFile"
|
||||
"alreadyExists"
|
||||
`shouldThrow`
|
||||
isFileDoesExist
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "renameFile, move from file to dir" $
|
||||
renameFile' "myFile"
|
||||
"alreadyExistsD"
|
||||
`shouldThrow`
|
||||
isDirDoesExist
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "renameFile, source and dest are same file" $
|
||||
renameFile' "myFile"
|
||||
|
||||
27
test/HPath/IO/ToAbsSpec.hs
Normal file
27
test/HPath/IO/ToAbsSpec.hs
Normal file
@@ -0,0 +1,27 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
|
||||
module HPath.IO.ToAbsSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath
|
||||
import HPath.IO
|
||||
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = describe "HPath.IO.toAbs" $ do
|
||||
|
||||
-- successes --
|
||||
it "toAbs returns absolute paths unchanged" $ do
|
||||
p1 <- parseAbs "/a/b/c/d"
|
||||
to <- toAbs p1
|
||||
p1 `shouldBe` to
|
||||
|
||||
it "toAbs returns even existing absolute paths unchanged" $ do
|
||||
p1 <- parseAbs "/home"
|
||||
to <- toAbs p1
|
||||
p1 `shouldBe` to
|
||||
|
||||
|
||||
108
test/HPath/IO/WriteFileSpec.hs
Normal file
108
test/HPath/IO/WriteFileSpec.hs
Normal file
@@ -0,0 +1,108 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
|
||||
module HPath.IO.WriteFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "WriteFileSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "fileWithContent"
|
||||
createRegularFile' "fileWithoutContent"
|
||||
createSymlink' "inputFileSymL" "fileWithContent"
|
||||
createDir' "alreadyExistsD"
|
||||
createRegularFile' "noPerms"
|
||||
noPerms "noPerms"
|
||||
createDir' "noPermsD"
|
||||
createRegularFile' "noPermsD/inputFile"
|
||||
noPerms "noPermsD"
|
||||
writeFile' "fileWithContent" "BLKASL"
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
deleteFile' "fileWithContent"
|
||||
deleteFile' "fileWithoutContent"
|
||||
deleteFile' "inputFileSymL"
|
||||
deleteDir' "alreadyExistsD"
|
||||
normalFilePerms "noPerms"
|
||||
deleteFile' "noPerms"
|
||||
normalDirPerms "noPermsD"
|
||||
deleteFile' "noPermsD/inputFile"
|
||||
deleteDir' "noPermsD"
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.writeFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "writeFile file with content, everything clear" $ do
|
||||
writeFile' "fileWithContent" "blahfaselllll"
|
||||
out <- readFile' "fileWithContent"
|
||||
out `shouldBe` "blahfaselllll"
|
||||
|
||||
it "writeFile file with content, everything clear" $ do
|
||||
writeFile' "fileWithContent" "gagagaga"
|
||||
out <- readFile' "fileWithContent"
|
||||
out `shouldBe` "gagagaga"
|
||||
|
||||
it "writeFile file with content, everything clear" $ do
|
||||
writeFile' "fileWithContent" ""
|
||||
out <- readFile' "fileWithContent"
|
||||
out `shouldBe` ""
|
||||
|
||||
it "writeFile file without content, everything clear" $ do
|
||||
writeFile' "fileWithoutContent" "blahfaselllll"
|
||||
out <- readFile' "fileWithoutContent"
|
||||
out `shouldBe` "blahfaselllll"
|
||||
|
||||
it "writeFile, everything clear" $ do
|
||||
writeFile' "fileWithoutContent" "gagagaga"
|
||||
out <- readFile' "fileWithoutContent"
|
||||
out `shouldBe` "gagagaga"
|
||||
|
||||
it "writeFile symlink, everything clear" $ do
|
||||
writeFile' "inputFileSymL" "blahfaselllll"
|
||||
out <- readFile' "inputFileSymL"
|
||||
out `shouldBe` "blahfaselllll"
|
||||
|
||||
it "writeFile symlink, everything clear" $ do
|
||||
writeFile' "inputFileSymL" "gagagaga"
|
||||
out <- readFile' "inputFileSymL"
|
||||
out `shouldBe` "gagagaga"
|
||||
|
||||
|
||||
-- posix failures --
|
||||
it "writeFile to dir, inappropriate type" $ do
|
||||
writeFile' "alreadyExistsD" ""
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "writeFile, no permissions to file" $ do
|
||||
writeFile' "noPerms" ""
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "writeFile, no permissions to file" $ do
|
||||
writeFile' "noPermsD/inputFile" ""
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "writeFile, file does not exist" $ do
|
||||
writeFile' "gaga" ""
|
||||
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
||||
@@ -14,10 +14,6 @@ main :: IO ()
|
||||
main =
|
||||
hspecWith
|
||||
defaultConfig { configFormatter = Just progress }
|
||||
$ before_ up
|
||||
$ after_ down
|
||||
$ beforeAll_ createBaseTmpDir
|
||||
$ afterAll_ deleteBaseTmpDir
|
||||
$ Spec.spec
|
||||
where
|
||||
up = createTmpDir
|
||||
down = deleteTmpDir
|
||||
|
||||
|
||||
184
test/Utils.hs
184
test/Utils.hs
@@ -11,16 +11,34 @@ import Control.Applicative
|
||||
)
|
||||
import Control.Monad
|
||||
(
|
||||
void
|
||||
forM_
|
||||
, void
|
||||
)
|
||||
import Control.Monad.IfElse
|
||||
(
|
||||
whenM
|
||||
)
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.IORef
|
||||
(
|
||||
newIORef
|
||||
, readIORef
|
||||
, writeIORef
|
||||
, IORef
|
||||
)
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import HPath.IO.Utils
|
||||
import Prelude hiding (appendFile, readFile, writeFile)
|
||||
import Data.Maybe
|
||||
(
|
||||
fromJust
|
||||
)
|
||||
import qualified HPath as P
|
||||
import System.IO.Unsafe
|
||||
(
|
||||
unsafePerformIO
|
||||
)
|
||||
import qualified System.Posix.Directory.Traversals as DT
|
||||
import System.Posix.Env.ByteString
|
||||
(
|
||||
getEnv
|
||||
@@ -29,6 +47,7 @@ import Data.ByteString
|
||||
(
|
||||
ByteString
|
||||
)
|
||||
import qualified Data.ByteString.Lazy as L
|
||||
import System.Posix.Files.ByteString
|
||||
(
|
||||
groupExecuteMode
|
||||
@@ -42,13 +61,15 @@ import System.Posix.Files.ByteString
|
||||
, unionFileModes
|
||||
)
|
||||
|
||||
import qualified "unix" System.Posix.IO.ByteString as SPI
|
||||
import qualified "unix-bytestring" System.Posix.IO.ByteString as SPB
|
||||
|
||||
|
||||
baseTmpDir :: ByteString
|
||||
baseTmpDir = "test/HPath/IO/tmp/"
|
||||
|
||||
tmpDir :: ByteString
|
||||
tmpDir = "test/HPath/IO/tmp/"
|
||||
|
||||
tmpDir :: IORef ByteString
|
||||
{-# NOINLINE tmpDir #-}
|
||||
tmpDir = unsafePerformIO (newIORef baseTmpDir)
|
||||
|
||||
|
||||
|
||||
@@ -57,31 +78,63 @@ tmpDir = "test/HPath/IO/tmp/"
|
||||
-----------------
|
||||
|
||||
|
||||
setTmpDir :: ByteString -> IO ()
|
||||
{-# NOINLINE setTmpDir #-}
|
||||
setTmpDir bs = writeIORef tmpDir (baseTmpDir `BS.append` bs)
|
||||
|
||||
|
||||
createTmpDir :: IO ()
|
||||
{-# NOINLINE createTmpDir #-}
|
||||
createTmpDir = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel tmpDir
|
||||
void $ createDir (pwd P.</> tmp)
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
void $ createDir newDirPerms (pwd P.</> tmp)
|
||||
|
||||
|
||||
deleteTmpDir :: IO ()
|
||||
{-# NOINLINE deleteTmpDir #-}
|
||||
deleteTmpDir = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel tmpDir
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
void $ deleteDir (pwd P.</> tmp)
|
||||
|
||||
|
||||
createBaseTmpDir :: IO ()
|
||||
{-# NOINLINE createBaseTmpDir #-}
|
||||
createBaseTmpDir = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel baseTmpDir
|
||||
void $ createDir newDirPerms (pwd P.</> tmp)
|
||||
|
||||
|
||||
deleteBaseTmpDir :: IO ()
|
||||
{-# NOINLINE deleteBaseTmpDir #-}
|
||||
deleteBaseTmpDir = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel baseTmpDir
|
||||
contents <- getDirsFiles (pwd P.</> tmp)
|
||||
forM_ contents deleteDir
|
||||
void $ deleteDir (pwd P.</> tmp)
|
||||
|
||||
|
||||
withRawTmpDir :: (P.Path P.Abs -> IO a) -> IO a
|
||||
{-# NOINLINE withRawTmpDir #-}
|
||||
withRawTmpDir f = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel tmpDir
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
f (pwd P.</> tmp)
|
||||
|
||||
|
||||
getRawTmpDir :: IO ByteString
|
||||
{-# NOINLINE getRawTmpDir #-}
|
||||
getRawTmpDir = withRawTmpDir (return . flip BS.append "/" . P.fromAbs)
|
||||
|
||||
|
||||
withTmpDir :: ByteString -> (P.Path P.Abs -> IO a) -> IO a
|
||||
{-# NOINLINE withTmpDir #-}
|
||||
withTmpDir ip f = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel tmpDir
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
p <- (pwd P.</> tmp P.</>) <$> P.parseRel ip
|
||||
f p
|
||||
|
||||
@@ -90,84 +143,83 @@ withTmpDir' :: ByteString
|
||||
-> ByteString
|
||||
-> (P.Path P.Abs -> P.Path P.Abs -> IO a)
|
||||
-> IO a
|
||||
{-# NOINLINE withTmpDir' #-}
|
||||
withTmpDir' ip1 ip2 f = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel tmpDir
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
p1 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip1
|
||||
p2 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip2
|
||||
f p1 p2
|
||||
|
||||
|
||||
removeFileIfExists :: ByteString -> IO ()
|
||||
{-# NOINLINE removeFileIfExists #-}
|
||||
removeFileIfExists bs =
|
||||
withTmpDir bs $ \p -> whenM (doesFileExist p) (deleteFile p)
|
||||
|
||||
|
||||
removeDirIfExists :: ByteString -> IO ()
|
||||
{-# NOINLINE removeDirIfExists #-}
|
||||
removeDirIfExists bs =
|
||||
withTmpDir bs $ \p -> whenM (doesDirectoryExist p) (deleteDirRecursive p)
|
||||
|
||||
|
||||
copyFile' :: ByteString -> ByteString -> IO ()
|
||||
copyFile' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP copyFile
|
||||
copyFile' :: ByteString -> ByteString -> CopyMode -> IO ()
|
||||
{-# NOINLINE copyFile' #-}
|
||||
copyFile' inputFileP outputFileP cm =
|
||||
withTmpDir' inputFileP outputFileP (\p1 p2 -> copyFile p1 p2 cm)
|
||||
|
||||
|
||||
copyFileOverwrite' :: ByteString -> ByteString -> IO ()
|
||||
copyFileOverwrite' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP copyFileOverwrite
|
||||
|
||||
|
||||
copyDirRecursive' :: ByteString -> ByteString -> IO ()
|
||||
copyDirRecursive' inputDirP outputDirP =
|
||||
withTmpDir' inputDirP outputDirP copyDirRecursive
|
||||
|
||||
|
||||
copyDirRecursiveOverwrite' :: ByteString -> ByteString -> IO ()
|
||||
copyDirRecursiveOverwrite' inputDirP outputDirP =
|
||||
withTmpDir' inputDirP outputDirP copyDirRecursiveOverwrite
|
||||
copyDirRecursive' :: ByteString -> ByteString
|
||||
-> CopyMode -> RecursiveErrorMode -> IO ()
|
||||
{-# NOINLINE copyDirRecursive' #-}
|
||||
copyDirRecursive' inputDirP outputDirP cm rm =
|
||||
withTmpDir' inputDirP outputDirP (\p1 p2 -> copyDirRecursive p1 p2 cm rm)
|
||||
|
||||
|
||||
createDir' :: ByteString -> IO ()
|
||||
createDir' dest = withTmpDir dest createDir
|
||||
{-# NOINLINE createDir' #-}
|
||||
createDir' dest = withTmpDir dest (createDir newDirPerms)
|
||||
|
||||
createDirRecursive' :: ByteString -> IO ()
|
||||
{-# NOINLINE createDirRecursive' #-}
|
||||
createDirRecursive' dest = withTmpDir dest (createDirRecursive newDirPerms)
|
||||
|
||||
createRegularFile' :: ByteString -> IO ()
|
||||
createRegularFile' dest = withTmpDir dest createRegularFile
|
||||
{-# NOINLINE createRegularFile' #-}
|
||||
createRegularFile' dest = withTmpDir dest (createRegularFile newFilePerms)
|
||||
|
||||
|
||||
createSymlink' :: ByteString -> ByteString -> IO ()
|
||||
{-# NOINLINE createSymlink' #-}
|
||||
createSymlink' dest sympoint = withTmpDir dest
|
||||
(\x -> createSymlink x sympoint)
|
||||
|
||||
|
||||
renameFile' :: ByteString -> ByteString -> IO ()
|
||||
{-# NOINLINE renameFile' #-}
|
||||
renameFile' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP $ \i o -> do
|
||||
renameFile i o
|
||||
renameFile o i
|
||||
|
||||
|
||||
moveFile' :: ByteString -> ByteString -> IO ()
|
||||
moveFile' inputFileP outputFileP =
|
||||
moveFile' :: ByteString -> ByteString -> CopyMode -> IO ()
|
||||
{-# NOINLINE moveFile' #-}
|
||||
moveFile' inputFileP outputFileP cm =
|
||||
withTmpDir' inputFileP outputFileP $ \i o -> do
|
||||
moveFile i o
|
||||
moveFile o i
|
||||
moveFile i o cm
|
||||
moveFile o i Strict
|
||||
|
||||
|
||||
moveFileOverwrite' :: ByteString -> ByteString -> IO ()
|
||||
moveFileOverwrite' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP $ \i o -> do
|
||||
moveFileOverwrite i o
|
||||
moveFile o i
|
||||
|
||||
|
||||
recreateSymlink' :: ByteString -> ByteString -> IO ()
|
||||
recreateSymlink' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP recreateSymlink
|
||||
recreateSymlink' :: ByteString -> ByteString -> CopyMode -> IO ()
|
||||
{-# NOINLINE recreateSymlink' #-}
|
||||
recreateSymlink' inputFileP outputFileP cm =
|
||||
withTmpDir' inputFileP outputFileP (\p1 p2 -> recreateSymlink p1 p2 cm)
|
||||
|
||||
|
||||
noWritableDirPerms :: ByteString -> IO ()
|
||||
{-# NOINLINE noWritableDirPerms #-}
|
||||
noWritableDirPerms path = withTmpDir path $ \p ->
|
||||
setFileMode (P.fromAbs p) perms
|
||||
where
|
||||
@@ -180,42 +232,76 @@ noWritableDirPerms path = withTmpDir path $ \p ->
|
||||
|
||||
|
||||
noPerms :: ByteString -> IO ()
|
||||
{-# NOINLINE noPerms #-}
|
||||
noPerms path = withTmpDir path $ \p -> setFileMode (P.fromAbs p) nullFileMode
|
||||
|
||||
|
||||
normalDirPerms :: ByteString -> IO ()
|
||||
{-# NOINLINE normalDirPerms #-}
|
||||
normalDirPerms path =
|
||||
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newDirPerms
|
||||
|
||||
|
||||
normalFilePerms :: ByteString -> IO ()
|
||||
{-# NOINLINE normalFilePerms #-}
|
||||
normalFilePerms path =
|
||||
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newFilePerms
|
||||
|
||||
|
||||
getFileType' :: ByteString -> IO FileType
|
||||
{-# NOINLINE getFileType' #-}
|
||||
getFileType' path = withTmpDir path getFileType
|
||||
|
||||
|
||||
getDirsFiles' :: ByteString -> IO [P.Path P.Abs]
|
||||
{-# NOINLINE getDirsFiles' #-}
|
||||
getDirsFiles' path = withTmpDir path getDirsFiles
|
||||
|
||||
|
||||
deleteFile' :: ByteString -> IO ()
|
||||
{-# NOINLINE deleteFile' #-}
|
||||
deleteFile' p = withTmpDir p deleteFile
|
||||
|
||||
|
||||
deleteDir' :: ByteString -> IO ()
|
||||
{-# NOINLINE deleteDir' #-}
|
||||
deleteDir' p = withTmpDir p deleteDir
|
||||
|
||||
|
||||
deleteDirRecursive' :: ByteString -> IO ()
|
||||
{-# NOINLINE deleteDirRecursive' #-}
|
||||
deleteDirRecursive' p = withTmpDir p deleteDirRecursive
|
||||
|
||||
|
||||
canonicalizePath' :: ByteString -> IO (P.Path P.Abs)
|
||||
{-# NOINLINE canonicalizePath' #-}
|
||||
canonicalizePath' p = withTmpDir p canonicalizePath
|
||||
|
||||
|
||||
writeFile' :: ByteString -> ByteString -> IO ()
|
||||
{-# NOINLINE writeFile' #-}
|
||||
writeFile' ip bs =
|
||||
withTmpDir ip $ \p -> do
|
||||
fd <- SPI.openFd (P.fromAbs p) SPI.WriteOnly Nothing
|
||||
SPI.defaultFileFlags
|
||||
SPB.fdWrite fd bs
|
||||
SPI.closeFd fd
|
||||
withTmpDir ip $ \p -> writeFile p bs
|
||||
|
||||
|
||||
appendFile' :: ByteString -> ByteString -> IO ()
|
||||
{-# NOINLINE appendFile' #-}
|
||||
appendFile' ip bs =
|
||||
withTmpDir ip $ \p -> appendFile p bs
|
||||
|
||||
|
||||
allDirectoryContents' :: ByteString -> IO [ByteString]
|
||||
{-# NOINLINE allDirectoryContents' #-}
|
||||
allDirectoryContents' ip =
|
||||
withTmpDir ip $ \p -> DT.allDirectoryContents' (P.fromAbs p)
|
||||
|
||||
|
||||
readFile' :: ByteString -> IO ByteString
|
||||
{-# NOINLINE readFile' #-}
|
||||
readFile' p = withTmpDir p readFile
|
||||
|
||||
|
||||
readFileEOF' :: ByteString -> IO L.ByteString
|
||||
{-# NOINLINE readFileEOF' #-}
|
||||
readFileEOF' p = withTmpDir p readFileEOF
|
||||
|
||||
|
||||
Reference in New Issue
Block a user