Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d71ad08ce | |||
| 92017ab630 | |||
| 16af98b32d | |||
| 6da01e382f | |||
| ed06543981 | |||
| d3eb2fc254 | |||
| a1eb06324f | |||
| d12ce30f57 | |||
| 7a6f0e8728 | |||
| 7ed5829d47 | |||
| d708f80a1f | |||
| f07619b7c6 | |||
| c5bcb90b65 | |||
| 4f047dbc77 | |||
| bc348c7dd5 | |||
| 5d1c5cc2ce | |||
| 8f6ca81d22 | |||
| a27d4ed55d | |||
| 64ae6db83a | |||
| 2a0a88a96d | |||
| 69dbf6714d | |||
| 2d96311b33 | |||
| 21668f12fe | |||
| 6e37e18bc8 | |||
| ae24f87c74 | |||
| 9f6734e700 | |||
| 741c510b91 | |||
| bb590a7692 | |||
| 641e23c3ef | |||
| 82ea75cc88 | |||
| abf043be14 | |||
| 10adc4be27 | |||
| a176e4970b | |||
| 08de2ebefb | |||
| d15d7761c1 | |||
| 7e924d3386 | |||
| 21fccc9ca9 | |||
| 79dbcd8b55 | |||
| b603f72407 | |||
| 98ca6c5d86 | |||
| 8d948366f9 | |||
| 86e7496917 | |||
| 1b9b8cc886 | |||
| 395621b27a | |||
| 51da8bf5c2 | |||
| bebc96fa6d |
18
.travis.yml
18
.travis.yml
@@ -9,15 +9,22 @@ matrix:
|
|||||||
include:
|
include:
|
||||||
- env: CABALVER=1.22 GHCVER=7.8.4
|
- env: CABALVER=1.22 GHCVER=7.8.4
|
||||||
addons: {apt: {packages: [cabal-install-1.22,ghc-7.8.4], sources: [hvr-ghc]}}
|
addons: {apt: {packages: [cabal-install-1.22,ghc-7.8.4], sources: [hvr-ghc]}}
|
||||||
- env: CABALVER=1.22 GHCVER=7.10.2
|
- env: CABALVER=1.24 GHCVER=7.10.2
|
||||||
addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}}
|
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=head GHCVER=head
|
- env: CABALVER=head GHCVER=head
|
||||||
addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}}
|
addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}}
|
||||||
|
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- env: CABALVER=head GHCVER=head
|
- env: CABALVER=head GHCVER=head
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- secure: q++z4DGwOHYjmed00oxMnGhBTzOBzKYunXvVcnCEmvmzW3qZERtXj3B7CLW4vRtmBlo3SiM0fb25NeYao+ByzTjo8jk9noiBVZvffwRmlKCeVwYx7T4/rsDhfV97k2JOeahBSgxWNuTkt+5gv07HpKdTiIxJsiv/QdBxQeq6/Ly6dyRskmCt+VuFvQg+cqPMugxIXtY6F7eZ1zgl/LxlamWjO3E4lX0Myf4o8+SU1HRDVkkVe+ytnRcVcYI2FHuFV/sSoDMTweXQToA9roVjOkfhq4rGlPCuXJkBPyZW2otLXgAV7I2kjwgxqmS5Yw752CcFjMMbG6R1u8sEAcGrJNKHfx8sKqBwI0AVoq4CJn+nKSElTDl0KI1mqazmazK4/mddkD9NGIVXCFmw4b+YGf1uDj8FAR94UmOiEFkEObGkQxG1XK/uzDaUJ1tO3MYXjPPEIE89BJORo+ZskmKFEoqbrBR/vEjbXxJHWP7SaaoM+mWpMiSssEFb/Z5mDBFPb2P/2f7nO4ZDfOYp/9hZdBvDaVM8FmTQfzF6jIUIOFmeeiSZWIBAHoDfdZDRrM/hC5JzqfMumW9frwllsQtYytkAsUqlNnCW86jlc5/5L6D8eY2NERFI2DRqrBi7bP2AfYXsozY0gMO1RL5+iQSQVKlPhk6IyAJYCWCYnrA+dz4=
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
|
- sudo apt-get install -y hscolour
|
||||||
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@@ -32,6 +39,7 @@ script:
|
|||||||
- cabal test
|
- cabal test
|
||||||
- cabal check
|
- cabal check
|
||||||
- cabal sdist
|
- cabal sdist
|
||||||
|
- cabal haddock --hyperlink-source --html-location=https://hackage.haskell.org/package/\$pkg-\$version/docs/
|
||||||
# check that the generated source-distribution can be built & installed
|
# check that the generated source-distribution can be built & installed
|
||||||
- export SRC_TGZ=$(cabal info . | awk '{print $2 ".tar.gz";exit}') ;
|
- export SRC_TGZ=$(cabal info . | awk '{print $2 ".tar.gz";exit}') ;
|
||||||
cd dist/;
|
cd dist/;
|
||||||
@@ -41,7 +49,11 @@ script:
|
|||||||
else
|
else
|
||||||
echo "expected '$SRC_TGZ' not found";
|
echo "expected '$SRC_TGZ' not found";
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi;
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- ./update-gh-pages.sh
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
|
|||||||
13
CHANGELOG
13
CHANGELOG
@@ -1,3 +1,16 @@
|
|||||||
|
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:
|
||||||
|
* fix tests, so they work with the sdist tarball too
|
||||||
|
* added the following function to HPath.IO: createSymlink
|
||||||
0.7.1:
|
0.7.1:
|
||||||
* various cleanups and documentation improvements
|
* various cleanups and documentation improvements
|
||||||
* added the following functions to System.Posix.FilePath: splitSearchPath, getSearchPath, stripExtension, makeRelative, makeValid
|
* added the following functions to System.Posix.FilePath: splitSearchPath, getSearchPath, stripExtension, makeRelative, makeValid
|
||||||
|
|||||||
358
LICENSE
358
LICENSE
@@ -1,340 +1,30 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
Copyright (c) Julian Ospald
|
||||||
Version 2, June 1991
|
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
All rights reserved.
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
1. Redistributions of source code must retain the above copyright
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
notice, this list of conditions and the following disclaimer.
|
||||||
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.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
notice, this list of conditions and the following disclaimer in the
|
||||||
have the freedom to distribute copies of free software (and charge for
|
documentation and/or other materials provided with the distribution.
|
||||||
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.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
3. Neither the name of the author nor the names of his contributors
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.
|
may be used to endorse or promote products derived from this software
|
||||||
These restrictions translate to certain responsibilities for you if you
|
without specific prior written permission.
|
||||||
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.
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -28,6 +28,8 @@ so it is forked as well and merged into this library.
|
|||||||
* safe filepath manipulation, never using String as filepath, but ByteString
|
* safe filepath manipulation, never using String as filepath, but ByteString
|
||||||
* still allowing sufficient control to interact with the underlying low-level calls
|
* still allowing sufficient control to interact with the underlying low-level calls
|
||||||
|
|
||||||
|
Note: this library was written for __posix__ systems and it will probably not support other systems.
|
||||||
|
|
||||||
## Differences to 'path'
|
## Differences to 'path'
|
||||||
|
|
||||||
* doesn't attempt to fake IO-related information into the path, so whether a path points to a file or directory is up to your IO-code to decide...
|
* doesn't attempt to fake IO-related information into the path, so whether a path points to a file or directory is up to your IO-code to decide...
|
||||||
@@ -60,3 +62,26 @@ so it is forked as well and merged into this library.
|
|||||||
* has a custom versions of `openFd` which allows more control over the flags than its unix package counterpart
|
* has a custom versions of `openFd` which allows more control over the flags than its unix package counterpart
|
||||||
* adds a `getDirectoryContents'` version that works on Fd
|
* adds a `getDirectoryContents'` version that works on Fd
|
||||||
|
|
||||||
|
## Examples in ghci
|
||||||
|
|
||||||
|
Start ghci via `cabal repl`:
|
||||||
|
|
||||||
|
```hs
|
||||||
|
-- enable OverloadedStrings
|
||||||
|
:set -XOverloadedStrings
|
||||||
|
-- import HPath.IO
|
||||||
|
import HPath.IO
|
||||||
|
-- parse an absolute path
|
||||||
|
abspath <- parseAbs "/home"
|
||||||
|
-- parse a relative path (e.g. user users home directory)
|
||||||
|
relpath <- parseRel "jule"
|
||||||
|
-- concatenate paths
|
||||||
|
let newpath = abspath </> relpath
|
||||||
|
-- get file type
|
||||||
|
getFileType newpath
|
||||||
|
-- return all contents of that directory
|
||||||
|
getDirsFiles newpath
|
||||||
|
-- return all contents of the parent directory
|
||||||
|
getDirsFiles (dirname newpath)
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE TupleSections #-}
|
|
||||||
{-# LANGUAGE ViewPatterns #-}
|
|
||||||
|
|
||||||
{-# OPTIONS_GHC -Wall #-}
|
|
||||||
import Control.Applicative
|
|
||||||
import Control.Monad
|
|
||||||
import System.Directory
|
|
||||||
import System.FilePath ((</>))
|
|
||||||
import System.Posix.ByteString.FilePath
|
|
||||||
import System.Posix.Directory.ByteString as PosixBS
|
|
||||||
import System.Posix.Directory.Traversals
|
|
||||||
import qualified System.Posix.FilePath as PosixBS
|
|
||||||
import System.Posix.Files.ByteString
|
|
||||||
|
|
||||||
import Control.Exception
|
|
||||||
import qualified Data.ByteString.Char8 as BS
|
|
||||||
|
|
||||||
import System.Environment (getArgs, withArgs)
|
|
||||||
import System.IO.Error
|
|
||||||
import System.IO.Unsafe
|
|
||||||
import System.Process (system)
|
|
||||||
import Criterion.Main
|
|
||||||
|
|
||||||
|
|
||||||
-- | Based on code from 'Real World Haskell', at
|
|
||||||
-- http://book.realworldhaskell.org/read/io-case-study-a-library-for-searching-the-filesystem.html#id620419
|
|
||||||
listFilesRecursive :: FilePath -> IO [FilePath]
|
|
||||||
listFilesRecursive topdir = do
|
|
||||||
names <- System.Directory.getDirectoryContents topdir
|
|
||||||
let properNames = filter (`notElem` [".", ".."]) names
|
|
||||||
paths <- forM properNames $ \name -> do
|
|
||||||
let path = topdir </> name
|
|
||||||
isDir <- doesDirectoryExist path
|
|
||||||
if isDir
|
|
||||||
then listFilesRecursive path
|
|
||||||
else return [path]
|
|
||||||
return (topdir : concat paths)
|
|
||||||
|
|
||||||
----------------------------------------------------------
|
|
||||||
|
|
||||||
getDirectoryContentsBS :: RawFilePath -> IO [RawFilePath]
|
|
||||||
getDirectoryContentsBS path =
|
|
||||||
modifyIOError ((`ioeSetFileName` (BS.unpack path)) .
|
|
||||||
(`ioeSetLocation` "getDirectoryContentsBS")) $ do
|
|
||||||
bracket
|
|
||||||
(PosixBS.openDirStream path)
|
|
||||||
PosixBS.closeDirStream
|
|
||||||
loop
|
|
||||||
where
|
|
||||||
loop dirp = do
|
|
||||||
e <- PosixBS.readDirStream dirp
|
|
||||||
if BS.null e then return [] else do
|
|
||||||
es <- loop dirp
|
|
||||||
return (e:es)
|
|
||||||
|
|
||||||
|
|
||||||
-- | similar to 'listFilesRecursive, but uses RawFilePaths
|
|
||||||
listFilesRecursiveBS :: RawFilePath -> IO [RawFilePath]
|
|
||||||
listFilesRecursiveBS topdir = do
|
|
||||||
names <- getDirectoryContentsBS topdir
|
|
||||||
let properNames = filter (`notElem` [".", ".."]) names
|
|
||||||
paths <- forM properNames $ \name -> unsafeInterleaveIO $ do
|
|
||||||
let path = PosixBS.combine topdir name
|
|
||||||
isDir <- isDirectory <$> getFileStatus path
|
|
||||||
if isDir
|
|
||||||
then listFilesRecursiveBS path
|
|
||||||
else return [path]
|
|
||||||
return (topdir : concat paths)
|
|
||||||
----------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
benchTraverse :: RawFilePath -> IO ()
|
|
||||||
benchTraverse = traverseDirectory (\() p -> BS.putStrLn p) ()
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = do
|
|
||||||
args <- getArgs
|
|
||||||
let (d,otherArgs) = case args of
|
|
||||||
[] -> ("/usr/local",[])
|
|
||||||
x:xs -> (x,xs)
|
|
||||||
withArgs otherArgs $ defaultMain
|
|
||||||
[ bench "traverse (FilePath)" $ nfIO $ listFilesRecursive d >>= mapM_ putStrLn
|
|
||||||
, bench "traverse (RawFilePath)" $ nfIO $ listFilesRecursiveBS (BS.pack d) >>= mapM_ BS.putStrLn
|
|
||||||
, bench "allDirectoryContents" $ nfIO $ allDirectoryContents (BS.pack d) >>= mapM_ BS.putStrLn
|
|
||||||
, bench "allDirectoryContents'" $ nfIO $ allDirectoryContents' (BS.pack d) >>= mapM_ BS.putStrLn
|
|
||||||
, bench "traverseDirectory" $ nfIO $ benchTraverse (BS.pack d)
|
|
||||||
, bench "unix find" $ nfIO $ void $ system ("find " ++ d)
|
|
||||||
]
|
|
||||||
42
hpath.cabal
42
hpath.cabal
@@ -1,8 +1,8 @@
|
|||||||
name: hpath
|
name: hpath
|
||||||
version: 0.7.1
|
version: 0.8.0
|
||||||
synopsis: Support for well-typed paths
|
synopsis: Support for well-typed paths
|
||||||
description: Support for well-typed paths, utilizing ByteString under the hood.
|
description: Support for well-typed paths, utilizing ByteString under the hood.
|
||||||
license: GPL-2
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Julian Ospald <hasufell@posteo.de>
|
author: Julian Ospald <hasufell@posteo.de>
|
||||||
maintainer: Julian Ospald <hasufell@posteo.de>
|
maintainer: Julian Ospald <hasufell@posteo.de>
|
||||||
@@ -12,7 +12,6 @@ build-type: Simple
|
|||||||
cabal-version: >=1.14
|
cabal-version: >=1.14
|
||||||
extra-source-files: README.md
|
extra-source-files: README.md
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
benchmarks/*.hs
|
|
||||||
cbits/dirutils.h
|
cbits/dirutils.h
|
||||||
doctests-hpath.hs
|
doctests-hpath.hs
|
||||||
doctests-posix.hs
|
doctests-posix.hs
|
||||||
@@ -20,17 +19,20 @@ extra-source-files: README.md
|
|||||||
library
|
library
|
||||||
hs-source-dirs: src/
|
hs-source-dirs: src/
|
||||||
default-language: Haskell2010
|
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
|
c-sources: cbits/dirutils.c
|
||||||
exposed-modules: HPath,
|
exposed-modules: HPath,
|
||||||
HPath.IO,
|
HPath.IO,
|
||||||
HPath.IO.Errors,
|
HPath.IO.Errors,
|
||||||
HPath.IO.Utils,
|
HPath.IO.Utils,
|
||||||
HPath.Internal,
|
|
||||||
System.Posix.Directory.Foreign,
|
System.Posix.Directory.Foreign,
|
||||||
System.Posix.Directory.Traversals,
|
System.Posix.Directory.Traversals,
|
||||||
System.Posix.FD,
|
System.Posix.FD,
|
||||||
System.Posix.FilePath
|
System.Posix.FilePath
|
||||||
|
other-modules: HPath.Internal
|
||||||
build-depends: base >= 4.2 && <5
|
build-depends: base >= 4.2 && <5
|
||||||
, bytestring >= 0.9.2.0
|
, bytestring >= 0.9.2.0
|
||||||
, deepseq
|
, deepseq
|
||||||
@@ -73,22 +75,26 @@ test-suite spec
|
|||||||
Hs-Source-Dirs: test
|
Hs-Source-Dirs: test
|
||||||
Main-Is: Main.hs
|
Main-Is: Main.hs
|
||||||
other-modules:
|
other-modules:
|
||||||
Spec
|
HPath.IO.CanonicalizePathSpec
|
||||||
HPath.IO.CopyDirRecursiveSpec
|
HPath.IO.CopyDirRecursiveCollectFailuresSpec
|
||||||
HPath.IO.CopyDirRecursiveOverwriteSpec
|
HPath.IO.CopyDirRecursiveOverwriteSpec
|
||||||
HPath.IO.CopyFileSpec
|
HPath.IO.CopyDirRecursiveSpec
|
||||||
HPath.IO.CopyFileOverwriteSpec
|
HPath.IO.CopyFileOverwriteSpec
|
||||||
|
HPath.IO.CopyFileSpec
|
||||||
HPath.IO.CreateDirSpec
|
HPath.IO.CreateDirSpec
|
||||||
HPath.IO.CreateRegularFileSpec
|
HPath.IO.CreateRegularFileSpec
|
||||||
|
HPath.IO.CreateSymlinkSpec
|
||||||
HPath.IO.DeleteDirRecursiveSpec
|
HPath.IO.DeleteDirRecursiveSpec
|
||||||
HPath.IO.DeleteDirSpec
|
HPath.IO.DeleteDirSpec
|
||||||
HPath.IO.DeleteFileSpec
|
HPath.IO.DeleteFileSpec
|
||||||
HPath.IO.GetDirsFilesSpec
|
HPath.IO.GetDirsFilesSpec
|
||||||
HPath.IO.GetFileTypeSpec
|
HPath.IO.GetFileTypeSpec
|
||||||
HPath.IO.MoveFileSpec
|
|
||||||
HPath.IO.MoveFileOverwriteSpec
|
HPath.IO.MoveFileOverwriteSpec
|
||||||
|
HPath.IO.MoveFileSpec
|
||||||
|
HPath.IO.RecreateSymlinkOverwriteSpec
|
||||||
HPath.IO.RecreateSymlinkSpec
|
HPath.IO.RecreateSymlinkSpec
|
||||||
HPath.IO.RenameFileSpec
|
HPath.IO.RenameFileSpec
|
||||||
|
Spec
|
||||||
Utils
|
Utils
|
||||||
GHC-Options: -Wall
|
GHC-Options: -Wall
|
||||||
Build-Depends: base
|
Build-Depends: base
|
||||||
@@ -98,25 +104,9 @@ test-suite spec
|
|||||||
, hspec >= 1.3
|
, hspec >= 1.3
|
||||||
, process
|
, process
|
||||||
, unix
|
, unix
|
||||||
|
, unix-bytestring
|
||||||
, utf8-string
|
, utf8-string
|
||||||
|
|
||||||
benchmark bench.hs
|
|
||||||
default-language: Haskell2010
|
|
||||||
type: exitcode-stdio-1.0
|
|
||||||
hs-source-dirs: benchmarks
|
|
||||||
main-is: Bench.hs
|
|
||||||
|
|
||||||
build-depends:
|
|
||||||
base,
|
|
||||||
hpath,
|
|
||||||
bytestring,
|
|
||||||
unix,
|
|
||||||
directory >= 1.1 && < 1.3,
|
|
||||||
filepath >= 1.2 && < 1.5,
|
|
||||||
process >= 1.0 && < 1.3,
|
|
||||||
criterion >= 0.6 && < 1.2
|
|
||||||
ghc-options: -O2
|
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/hasufell/hpath
|
location: https://github.com/hasufell/hpath
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ module HPath
|
|||||||
,Fn
|
,Fn
|
||||||
,PathParseException
|
,PathParseException
|
||||||
,PathException
|
,PathException
|
||||||
|
,RelC
|
||||||
-- * PatternSynonyms/ViewPatterns
|
-- * PatternSynonyms/ViewPatterns
|
||||||
,pattern Path
|
,pattern Path
|
||||||
-- * Path Parsing
|
-- * Path Parsing
|
||||||
@@ -89,12 +90,17 @@ data PathException = RootDirHasNoBasename
|
|||||||
deriving (Show,Typeable)
|
deriving (Show,Typeable)
|
||||||
instance Exception PathException
|
instance Exception PathException
|
||||||
|
|
||||||
|
class RelC m
|
||||||
|
|
||||||
instance RelC Rel
|
instance RelC Rel
|
||||||
instance RelC Fn
|
instance RelC Fn
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- PatternSynonyms
|
-- PatternSynonyms
|
||||||
|
|
||||||
|
#if __GLASGOW_HASKELL__ >= 710
|
||||||
|
pattern Path :: ByteString -> Path a
|
||||||
|
#endif
|
||||||
pattern Path x <- (MkPath x)
|
pattern Path x <- (MkPath x)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
468
src/HPath/IO.hs
468
src/HPath/IO.hs
@@ -1,7 +1,7 @@
|
|||||||
-- |
|
-- |
|
||||||
-- Module : HPath.IO
|
-- Module : HPath.IO
|
||||||
-- Copyright : © 2016 Julian Ospald
|
-- Copyright : © 2016 Julian Ospald
|
||||||
-- License : GPL-2
|
-- License : BSD3
|
||||||
--
|
--
|
||||||
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
||||||
-- Stability : experimental
|
-- Stability : experimental
|
||||||
@@ -26,9 +26,9 @@
|
|||||||
-- exception handling is kept.
|
-- exception handling is kept.
|
||||||
--
|
--
|
||||||
-- Note: `BlockDevice`, `CharacterDevice`, `NamedPipe` and `Socket`
|
-- Note: `BlockDevice`, `CharacterDevice`, `NamedPipe` and `Socket`
|
||||||
-- are not explicitly supported right now. Calling any of these
|
-- are ignored by some of the more high-level functions (like `easyCopy`).
|
||||||
-- functions on such a file may throw an exception or just do
|
-- For other functions (like `copyFile`), the behavior on these file types is
|
||||||
-- nothing.
|
-- unreliable/unsafe. Check the documentation of those functions for details.
|
||||||
|
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
@@ -37,14 +37,13 @@ module HPath.IO
|
|||||||
(
|
(
|
||||||
-- * Types
|
-- * Types
|
||||||
FileType(..)
|
FileType(..)
|
||||||
|
, RecursiveErrorMode(..)
|
||||||
|
, CopyMode(..)
|
||||||
-- * File copying
|
-- * File copying
|
||||||
, copyDirRecursive
|
, copyDirRecursive
|
||||||
, copyDirRecursiveOverwrite
|
|
||||||
, recreateSymlink
|
, recreateSymlink
|
||||||
, copyFile
|
, copyFile
|
||||||
, copyFileOverwrite
|
|
||||||
, easyCopy
|
, easyCopy
|
||||||
, easyCopyOverwrite
|
|
||||||
-- * File deletion
|
-- * File deletion
|
||||||
, deleteFile
|
, deleteFile
|
||||||
, deleteDir
|
, deleteDir
|
||||||
@@ -56,10 +55,10 @@ module HPath.IO
|
|||||||
-- * File creation
|
-- * File creation
|
||||||
, createRegularFile
|
, createRegularFile
|
||||||
, createDir
|
, createDir
|
||||||
|
, createSymlink
|
||||||
-- * File renaming/moving
|
-- * File renaming/moving
|
||||||
, renameFile
|
, renameFile
|
||||||
, moveFile
|
, moveFile
|
||||||
, moveFileOverwrite
|
|
||||||
-- * File permissions
|
-- * File permissions
|
||||||
, newFilePerms
|
, newFilePerms
|
||||||
, newDirPerms
|
, newDirPerms
|
||||||
@@ -79,12 +78,14 @@ import Control.Applicative
|
|||||||
)
|
)
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
(
|
(
|
||||||
bracket
|
IOException
|
||||||
|
, bracket
|
||||||
, throwIO
|
, throwIO
|
||||||
)
|
)
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
(
|
(
|
||||||
void
|
unless
|
||||||
|
, void
|
||||||
, when
|
, when
|
||||||
)
|
)
|
||||||
import Data.ByteString
|
import Data.ByteString
|
||||||
@@ -95,6 +96,13 @@ import Data.Foldable
|
|||||||
(
|
(
|
||||||
for_
|
for_
|
||||||
)
|
)
|
||||||
|
import Data.IORef
|
||||||
|
(
|
||||||
|
IORef
|
||||||
|
, modifyIORef
|
||||||
|
, newIORef
|
||||||
|
, readIORef
|
||||||
|
)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
(
|
(
|
||||||
catMaybes
|
catMaybes
|
||||||
@@ -130,7 +138,6 @@ import GHC.IO.Exception
|
|||||||
import HPath
|
import HPath
|
||||||
import HPath.Internal
|
import HPath.Internal
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import HPath.IO.Utils
|
|
||||||
import Prelude hiding (readFile)
|
import Prelude hiding (readFile)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@@ -212,6 +219,26 @@ data FileType = Directory
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- |The error mode for any recursive operation.
|
||||||
|
--
|
||||||
|
-- On `FailEarly` the whole operation fails immediately if any of the
|
||||||
|
-- recursive sub-operations fail, which is sort of the default
|
||||||
|
-- for IO operations.
|
||||||
|
--
|
||||||
|
-- On `CollectFailures` skips errors in the recursion and keeps on recursing.
|
||||||
|
-- However all errors are collected in the `RecursiveFailure` error type,
|
||||||
|
-- which is raised finally if there was any error.
|
||||||
|
data RecursiveErrorMode = FailEarly
|
||||||
|
| CollectFailures
|
||||||
|
|
||||||
|
|
||||||
|
-- |The mode for copy and file moves.
|
||||||
|
-- Overwrite mode is usually not very well defined, but is a convenience
|
||||||
|
-- shortcut.
|
||||||
|
data CopyMode = Strict -- ^ fail if any target exists
|
||||||
|
| Overwrite -- ^ overwrite targets
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
--------------------
|
||||||
@@ -220,8 +247,24 @@ data FileType = Directory
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- |Copies a directory recursively to the given destination.
|
-- |Copies the contents of a directory recursively to the given destination.
|
||||||
-- Does not follow symbolic links.
|
-- Does not follow symbolic links. This behaves more or less like:
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- mkdir \/destination\/dir
|
||||||
|
-- cp -R \/source\/dir\/* \/destination\/dir\/
|
||||||
|
-- @
|
||||||
|
--
|
||||||
|
-- For directory contents, this will ignore any file type that is not
|
||||||
|
-- `RegularFile`, `SymbolicLink` or `Directory`.
|
||||||
|
--
|
||||||
|
-- For `Overwrite` copy mode this does not prune destination directory
|
||||||
|
-- contents, so the destination might contain more files than the source after
|
||||||
|
-- the operation has completed. Permissions of existing directories are
|
||||||
|
-- fixed.
|
||||||
|
--
|
||||||
|
-- Note that there is no guaranteed ordering of the exceptions
|
||||||
|
-- contained within `RecursiveFailure` in `CollectFailures` RecursiveErrorMode.
|
||||||
--
|
--
|
||||||
-- Safety/reliability concerns:
|
-- Safety/reliability concerns:
|
||||||
--
|
--
|
||||||
@@ -235,105 +278,123 @@ data FileType = Directory
|
|||||||
-- Throws:
|
-- Throws:
|
||||||
--
|
--
|
||||||
-- - `NoSuchThing` if source directory does not exist
|
-- - `NoSuchThing` if source directory does not exist
|
||||||
-- - `PermissionDenied` if output directory is not writable
|
|
||||||
-- - `PermissionDenied` if source directory can't be opened
|
-- - `PermissionDenied` if source directory can't be opened
|
||||||
|
-- - `SameFile` if source and destination are the same file
|
||||||
|
-- (`HPathIOException`)
|
||||||
|
-- - `DestinationInSource` if destination is contained in source
|
||||||
|
-- (`HPathIOException`)
|
||||||
|
--
|
||||||
|
-- Throws in `FailEarly` RecursiveErrorMode only:
|
||||||
|
--
|
||||||
|
-- - `PermissionDenied` if output directory is not writable
|
||||||
-- - `InvalidArgument` if source directory is wrong type (symlink)
|
-- - `InvalidArgument` if source directory is wrong type (symlink)
|
||||||
-- - `InvalidArgument` if source directory is wrong type (regular file)
|
-- - `InappropriateType` if source directory is wrong type (regular file)
|
||||||
|
--
|
||||||
|
-- Throws in `CollectFailures` RecursiveErrorMode only:
|
||||||
|
--
|
||||||
|
-- - `RecursiveFailure` if any of the recursive operations that are not
|
||||||
|
-- part of the top-directory sanity-checks fail (`HPathIOException`)
|
||||||
|
--
|
||||||
|
-- Throws in `Strict` CopyMode only:
|
||||||
|
--
|
||||||
-- - `AlreadyExists` if destination already exists
|
-- - `AlreadyExists` if destination already exists
|
||||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
copyDirRecursive :: Path Abs -- ^ copy contents of this source dir
|
||||||
-- - `DestinationInSource` if destination is contained in source (`HPathIOException`)
|
-> Path Abs -- ^ to this full destination (parent dirs
|
||||||
copyDirRecursive :: Path Abs -- ^ source dir
|
-- are not automatically created)
|
||||||
-> Path Abs -- ^ full destination
|
-> CopyMode
|
||||||
|
-> RecursiveErrorMode
|
||||||
-> IO ()
|
-> IO ()
|
||||||
copyDirRecursive fromp destdirp
|
copyDirRecursive fromp destdirp cm rm
|
||||||
= do
|
= do
|
||||||
|
ce <- newIORef []
|
||||||
-- for performance, sanity checks are only done for the top dir
|
-- for performance, sanity checks are only done for the top dir
|
||||||
throwSameFile fromp destdirp
|
throwSameFile fromp destdirp
|
||||||
throwDestinationInSource fromp destdirp
|
throwDestinationInSource fromp destdirp
|
||||||
go fromp destdirp
|
go ce fromp destdirp
|
||||||
|
collectedExceptions <- readIORef ce
|
||||||
|
unless (null collectedExceptions)
|
||||||
|
(throwIO . RecursiveFailure $ collectedExceptions)
|
||||||
where
|
where
|
||||||
go :: Path Abs -> Path Abs -> IO ()
|
go :: IORef [IOException] -> Path Abs -> Path Abs -> IO ()
|
||||||
go fromp' destdirp' = do
|
go ce fromp' destdirp' = do
|
||||||
|
|
||||||
-- order is important here, so we don't get empty directories
|
-- order is important here, so we don't get empty directories
|
||||||
-- on failure
|
-- on failure
|
||||||
contents <- getDirsFiles fromp'
|
contents <- handleIOE ce [] $ do
|
||||||
|
contents <- getDirsFiles fromp'
|
||||||
|
|
||||||
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp')
|
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp')
|
||||||
createDirectory (fromAbs destdirp') fmode'
|
case cm of
|
||||||
|
Strict -> createDirectory (fromAbs destdirp') fmode'
|
||||||
|
Overwrite -> catchIOError (createDirectory (fromAbs destdirp')
|
||||||
|
fmode')
|
||||||
|
$ \e ->
|
||||||
|
case ioeGetErrorType e of
|
||||||
|
AlreadyExists -> setFileMode (fromAbs destdirp')
|
||||||
|
fmode'
|
||||||
|
_ -> ioError e
|
||||||
|
return contents
|
||||||
|
|
||||||
|
-- we can't use `easyCopy` here, because we want to call `go`
|
||||||
|
-- recursively to skip the top-level sanity checks
|
||||||
for_ contents $ \f -> do
|
for_ contents $ \f -> do
|
||||||
ftype <- getFileType f
|
ftype <- getFileType f
|
||||||
newdest <- (destdirp' </>) <$> basename f
|
newdest <- (destdirp' </>) <$> basename f
|
||||||
case ftype of
|
case ftype of
|
||||||
SymbolicLink -> recreateSymlink f newdest
|
SymbolicLink -> handleIOE ce ()
|
||||||
Directory -> go f newdest
|
$ recreateSymlink f newdest cm
|
||||||
RegularFile -> copyFile f newdest
|
Directory -> go ce f newdest
|
||||||
|
RegularFile -> handleIOE ce () $ copyFile f newdest cm
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
handleIOE :: IORef [IOException] -> a -> IO a -> IO a
|
||||||
|
handleIOE ce def = case rm of
|
||||||
|
FailEarly -> handleIOError throwIO
|
||||||
|
CollectFailures -> handleIOError (\e -> modifyIORef ce (e:)
|
||||||
|
>> return def)
|
||||||
|
|
||||||
|
|
||||||
-- |Like `copyDirRecursive` except it overwrites contents of directories
|
|
||||||
-- if any.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if source directory does not exist
|
|
||||||
-- - `PermissionDenied` if output directory is not writable
|
|
||||||
-- - `PermissionDenied` if source directory can't be opened
|
|
||||||
-- - `InvalidArgument` if source directory is wrong type (symlink)
|
|
||||||
-- - `InvalidArgument` if source directory is wrong type (regular file)
|
|
||||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
|
||||||
-- - `DestinationInSource` if destination is contained in source (`HPathIOException`)
|
|
||||||
copyDirRecursiveOverwrite :: Path Abs -- ^ source dir
|
|
||||||
-> Path Abs -- ^ full destination
|
|
||||||
-> IO ()
|
|
||||||
copyDirRecursiveOverwrite fromp destdirp
|
|
||||||
= do
|
|
||||||
-- for performance, sanity checks are only done for the top dir
|
|
||||||
throwSameFile fromp destdirp
|
|
||||||
throwDestinationInSource fromp destdirp
|
|
||||||
go fromp destdirp
|
|
||||||
where
|
|
||||||
go :: Path Abs -> Path Abs -> IO ()
|
|
||||||
go fromp' destdirp' = do
|
|
||||||
-- order is important here, so we don't get empty directories
|
|
||||||
-- on failure
|
|
||||||
contents <- getDirsFiles fromp'
|
|
||||||
|
|
||||||
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp')
|
|
||||||
catchIOError (createDirectory (fromAbs destdirp') fmode') $ \e ->
|
|
||||||
case ioeGetErrorType e of
|
|
||||||
AlreadyExists -> setFileMode (fromAbs destdirp') fmode'
|
|
||||||
_ -> ioError e
|
|
||||||
|
|
||||||
for_ contents $ \f -> do
|
|
||||||
ftype <- getFileType f
|
|
||||||
newdest <- (destdirp' </>) <$> basename f
|
|
||||||
case ftype of
|
|
||||||
SymbolicLink -> whenM (doesFileExist newdest) (deleteFile newdest)
|
|
||||||
>> recreateSymlink f newdest
|
|
||||||
Directory -> go f newdest
|
|
||||||
RegularFile -> copyFileOverwrite f newdest
|
|
||||||
_ -> return ()
|
|
||||||
|
|
||||||
-- |Recreate a symlink.
|
-- |Recreate a symlink.
|
||||||
--
|
--
|
||||||
|
-- In `Overwrite` copy mode only files and empty directories are deleted.
|
||||||
|
--
|
||||||
|
-- Safety/reliability concerns:
|
||||||
|
--
|
||||||
|
-- * `Overwrite` mode is inherently non-atomic
|
||||||
|
--
|
||||||
-- Throws:
|
-- Throws:
|
||||||
--
|
--
|
||||||
-- - `InvalidArgument` if symlink file is wrong type (file)
|
-- - `InvalidArgument` if source file is wrong type (not a symlink)
|
||||||
-- - `InvalidArgument` if symlink file is wrong type (directory)
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
-- - `PermissionDenied` if output directory cannot be written to
|
||||||
-- - `PermissionDenied` if source directory cannot be opened
|
-- - `PermissionDenied` if source directory cannot be opened
|
||||||
|
-- - `SameFile` if source and destination are the same file
|
||||||
|
-- (`HPathIOException`)
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- Throws in `Strict` mode only:
|
||||||
|
--
|
||||||
-- - `AlreadyExists` if destination file already exists
|
-- - `AlreadyExists` if destination file already exists
|
||||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
--
|
||||||
|
-- Throws in `Overwrite` mode only:
|
||||||
|
--
|
||||||
|
-- - `UnsatisfiedConstraints` if destination file is non-empty directory
|
||||||
--
|
--
|
||||||
-- Note: calls `symlink`
|
-- Note: calls `symlink`
|
||||||
recreateSymlink :: Path Abs -- ^ the old symlink file
|
recreateSymlink :: Path Abs -- ^ the old symlink file
|
||||||
-> Path Abs -- ^ destination file
|
-> Path Abs -- ^ destination file
|
||||||
|
-> CopyMode
|
||||||
-> IO ()
|
-> IO ()
|
||||||
recreateSymlink symsource newsym
|
recreateSymlink symsource newsym cm
|
||||||
= do
|
= do
|
||||||
throwSameFile symsource newsym
|
throwSameFile symsource newsym
|
||||||
sympoint <- readSymbolicLink (fromAbs symsource)
|
sympoint <- readSymbolicLink (fromAbs symsource)
|
||||||
|
case cm of
|
||||||
|
Strict -> return ()
|
||||||
|
Overwrite -> do
|
||||||
|
writable <- isWritable (dirname newsym)
|
||||||
|
isfile <- doesFileExist newsym
|
||||||
|
isdir <- doesDirectoryExist newsym
|
||||||
|
when (writable && isfile) (deleteFile newsym)
|
||||||
|
when (writable && isdir) (deleteDir newsym)
|
||||||
createSymbolicLink sympoint (fromAbs newsym)
|
createSymbolicLink sympoint (fromAbs newsym)
|
||||||
|
|
||||||
|
|
||||||
@@ -341,63 +402,62 @@ recreateSymlink symsource newsym
|
|||||||
-- Neither follows symbolic links, nor accepts them.
|
-- Neither follows symbolic links, nor accepts them.
|
||||||
-- For "copying" symbolic links, use `recreateSymlink` instead.
|
-- For "copying" symbolic links, use `recreateSymlink` instead.
|
||||||
--
|
--
|
||||||
|
-- Note that this is still sort of a low-level function and doesn't
|
||||||
|
-- examine file types. For a more high-level version, use `easyCopy`
|
||||||
|
-- instead.
|
||||||
|
--
|
||||||
|
-- In `Overwrite` copy mode only overwrites actual files, not directories.
|
||||||
|
--
|
||||||
|
-- Safety/reliability concerns:
|
||||||
|
--
|
||||||
|
-- * `Overwrite` mode is not atomic
|
||||||
|
-- * when used on `CharacterDevice`, reads the "contents" and copies
|
||||||
|
-- them to a regular file, which might take indefinitely
|
||||||
|
-- * when used on `BlockDevice`, may either read the "contents"
|
||||||
|
-- and copy them to a regular file (potentially hanging indefinitely)
|
||||||
|
-- or may create a regular empty destination file
|
||||||
|
-- * when used on `NamedPipe`, will hang indefinitely
|
||||||
|
--
|
||||||
-- Throws:
|
-- Throws:
|
||||||
--
|
--
|
||||||
-- - `NoSuchThing` if source file does not exist
|
-- - `NoSuchThing` if source file does not exist
|
||||||
|
-- - `NoSuchThing` if source file is a a `Socket`
|
||||||
-- - `PermissionDenied` if output directory is not writable
|
-- - `PermissionDenied` if output directory is not writable
|
||||||
-- - `PermissionDenied` if source directory can't be opened
|
-- - `PermissionDenied` if source directory can't be opened
|
||||||
-- - `InvalidArgument` if source file is wrong type (symlink)
|
-- - `InvalidArgument` if source file is wrong type (symlink or directory)
|
||||||
-- - `InvalidArgument` if source file is wrong type (directory)
|
-- - `SameFile` if source and destination are the same file
|
||||||
|
-- (`HPathIOException`)
|
||||||
|
--
|
||||||
|
-- Throws in `Strict` mode only:
|
||||||
|
--
|
||||||
-- - `AlreadyExists` if destination already exists
|
-- - `AlreadyExists` if destination already exists
|
||||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
|
||||||
--
|
--
|
||||||
-- Note: calls `sendfile` and possibly `read`/`write` as fallback
|
-- Note: calls `sendfile` and possibly `read`/`write` as fallback
|
||||||
copyFile :: Path Abs -- ^ source file
|
copyFile :: Path Abs -- ^ source file
|
||||||
-> Path Abs -- ^ destination file
|
-> Path Abs -- ^ destination file
|
||||||
|
-> CopyMode
|
||||||
-> IO ()
|
-> IO ()
|
||||||
copyFile from to = do
|
copyFile from to cm = do
|
||||||
throwSameFile from to
|
throwSameFile from to
|
||||||
_copyFile [SPDF.oNofollow]
|
|
||||||
[SPDF.oNofollow, SPDF.oExcl]
|
|
||||||
from to
|
|
||||||
|
|
||||||
|
case cm of
|
||||||
-- |Like `copyFile` except it overwrites the destination if it already
|
Strict -> _copyFile [SPDF.oNofollow]
|
||||||
-- exists.
|
[SPDF.oNofollow, SPDF.oExcl]
|
||||||
-- This also works if source and destination are the same file.
|
from to
|
||||||
--
|
Overwrite ->
|
||||||
-- Safety/reliability concerns:
|
catchIOError (_copyFile [SPDF.oNofollow]
|
||||||
--
|
[SPDF.oNofollow, SPDF.oTrunc]
|
||||||
-- * not atomic, since it uses read/write
|
from to) $ \e ->
|
||||||
--
|
case ioeGetErrorType e of
|
||||||
-- Throws:
|
-- if the destination file is not writable, we need to
|
||||||
--
|
-- figure out if we can still copy by deleting it first
|
||||||
-- - `NoSuchThing` if source file does not exist
|
PermissionDenied -> do
|
||||||
-- - `PermissionDenied` if output directory is not writable
|
exists <- doesFileExist to
|
||||||
-- - `PermissionDenied` if source directory can't be opened
|
writable <- isWritable (dirname to)
|
||||||
-- - `InvalidArgument` if source file is wrong type (symlink)
|
if exists && writable
|
||||||
-- - `InvalidArgument` if source file is wrong type (directory)
|
then deleteFile to >> copyFile from to Strict
|
||||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
else ioError e
|
||||||
--
|
_ -> ioError e
|
||||||
-- Note: calls `sendfile` and possibly `read`/`write` as fallback
|
|
||||||
copyFileOverwrite :: Path Abs -- ^ source file
|
|
||||||
-> Path Abs -- ^ destination file
|
|
||||||
-> IO ()
|
|
||||||
copyFileOverwrite from to = do
|
|
||||||
throwSameFile from to
|
|
||||||
catchIOError (_copyFile [SPDF.oNofollow]
|
|
||||||
[SPDF.oNofollow, SPDF.oTrunc]
|
|
||||||
from to) $ \e ->
|
|
||||||
case ioeGetErrorType e of
|
|
||||||
-- if the destination file is not writable, we need to
|
|
||||||
-- figure out if we can still copy by deleting it first
|
|
||||||
PermissionDenied -> do
|
|
||||||
exists <- doesFileExist to
|
|
||||||
writable <- isWritable (dirname to)
|
|
||||||
if exists && writable
|
|
||||||
then deleteFile to >> copyFile from to
|
|
||||||
else ioError e
|
|
||||||
_ -> ioError e
|
|
||||||
|
|
||||||
|
|
||||||
_copyFile :: [SPDF.Flags]
|
_copyFile :: [SPDF.Flags]
|
||||||
@@ -408,8 +468,8 @@ _copyFile :: [SPDF.Flags]
|
|||||||
_copyFile sflags dflags from to
|
_copyFile sflags dflags from to
|
||||||
=
|
=
|
||||||
-- from sendfile(2) manpage:
|
-- from sendfile(2) manpage:
|
||||||
-- Applications may wish to fall back to read(2)/write(2) in the case
|
-- Applications may wish to fall back to read(2)/write(2) in
|
||||||
-- where sendfile() fails with EINVAL or ENOSYS.
|
-- the case where sendfile() fails with EINVAL or ENOSYS.
|
||||||
withAbsPath to $ \to' -> withAbsPath from $ \from' ->
|
withAbsPath to $ \to' -> withAbsPath from $ \from' ->
|
||||||
catchErrno [eINVAL, eNOSYS]
|
catchErrno [eINVAL, eNOSYS]
|
||||||
(sendFileCopy from' to')
|
(sendFileCopy from' to')
|
||||||
@@ -445,12 +505,14 @@ _copyFile sflags dflags from to
|
|||||||
if size == 0
|
if size == 0
|
||||||
then return $ fromIntegral totalsize
|
then return $ fromIntegral totalsize
|
||||||
else do rsize <- SPB.fdWriteBuf dfd buf size
|
else do rsize <- SPB.fdWriteBuf dfd buf size
|
||||||
when (rsize /= size) (throwIO . CopyFailed $ "wrong size!")
|
when (rsize /= size) (throwIO . CopyFailed
|
||||||
|
$ "wrong size!")
|
||||||
write' sfd dfd buf (totalsize + fromIntegral size)
|
write' sfd dfd buf (totalsize + fromIntegral size)
|
||||||
|
|
||||||
|
|
||||||
-- |Copies anything. In case of a symlink,
|
-- |Copies a regular file, directory or symbolic link. In case of a
|
||||||
-- it is just recreated, even if it points to a directory.
|
-- symbolic link it is just recreated, even if it points to a directory.
|
||||||
|
-- Any other file type is ignored.
|
||||||
--
|
--
|
||||||
-- Safety/reliability concerns:
|
-- Safety/reliability concerns:
|
||||||
--
|
--
|
||||||
@@ -458,33 +520,18 @@ _copyFile sflags dflags from to
|
|||||||
-- * calls `copyDirRecursive` for directories
|
-- * calls `copyDirRecursive` for directories
|
||||||
easyCopy :: Path Abs
|
easyCopy :: Path Abs
|
||||||
-> Path Abs
|
-> Path Abs
|
||||||
|
-> CopyMode
|
||||||
|
-> RecursiveErrorMode
|
||||||
-> IO ()
|
-> IO ()
|
||||||
easyCopy from to = do
|
easyCopy from to cm rm = do
|
||||||
ftype <- getFileType from
|
ftype <- getFileType from
|
||||||
case ftype of
|
case ftype of
|
||||||
SymbolicLink -> recreateSymlink from to
|
SymbolicLink -> recreateSymlink from to cm
|
||||||
RegularFile -> copyFile from to
|
RegularFile -> copyFile from to cm
|
||||||
Directory -> copyDirRecursive from to
|
Directory -> copyDirRecursive from to cm rm
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
|
||||||
|
|
||||||
-- |Like `easyCopy` except it overwrites the destination if it already exists.
|
|
||||||
-- For directories, this overwrites contents without pruning them, so the resulting
|
|
||||||
-- directory may have more files than have been copied.
|
|
||||||
easyCopyOverwrite :: Path Abs
|
|
||||||
-> Path Abs
|
|
||||||
-> IO ()
|
|
||||||
easyCopyOverwrite from to = do
|
|
||||||
ftype <- getFileType from
|
|
||||||
case ftype of
|
|
||||||
SymbolicLink -> whenM (doesFileExist to) (deleteFile to)
|
|
||||||
>> recreateSymlink from to
|
|
||||||
RegularFile -> copyFileOverwrite from to
|
|
||||||
Directory -> copyDirRecursiveOverwrite from to
|
|
||||||
_ -> return ()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -524,6 +571,10 @@ deleteDir p = withAbsPath p removeDirectory
|
|||||||
-- links. Tries `deleteDir` first before attemtping a recursive
|
-- links. Tries `deleteDir` first before attemtping a recursive
|
||||||
-- deletion.
|
-- deletion.
|
||||||
--
|
--
|
||||||
|
-- On directory contents this behaves like `easyDelete`
|
||||||
|
-- and thus will ignore any file type that is not `RegularFile`,
|
||||||
|
-- `SymbolicLink` or `Directory`.
|
||||||
|
--
|
||||||
-- Safety/reliability concerns:
|
-- Safety/reliability concerns:
|
||||||
--
|
--
|
||||||
-- * not atomic
|
-- * not atomic
|
||||||
@@ -551,9 +602,10 @@ deleteDirRecursive p =
|
|||||||
removeDirectory . toFilePath $ p
|
removeDirectory . toFilePath $ p
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes a file, directory or symlink, whatever it may be.
|
-- |Deletes a file, directory or symlink.
|
||||||
-- In case of directory, performs recursive deletion. In case of
|
-- In case of directory, performs recursive deletion. In case of
|
||||||
-- a symlink, the symlink file is deleted.
|
-- a symlink, the symlink file is deleted.
|
||||||
|
-- Any other file type is ignored.
|
||||||
--
|
--
|
||||||
-- Safety/reliability concerns:
|
-- Safety/reliability concerns:
|
||||||
--
|
--
|
||||||
@@ -602,15 +654,16 @@ executeFile fp args
|
|||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
-- |Create an empty regular file at the given directory with the given filename.
|
-- |Create an empty regular file at the given directory with the given
|
||||||
|
-- filename.
|
||||||
--
|
--
|
||||||
-- Throws:
|
-- Throws:
|
||||||
--
|
--
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
-- - `PermissionDenied` if output directory cannot be written to
|
||||||
-- - `AlreadyExists` if destination file already exists
|
-- - `AlreadyExists` if destination file already exists
|
||||||
createRegularFile :: Path Abs -> IO ()
|
createRegularFile :: FileMode -> Path Abs -> IO ()
|
||||||
createRegularFile dest =
|
createRegularFile fm dest =
|
||||||
bracket (SPI.openFd (fromAbs dest) SPI.WriteOnly (Just newFilePerms)
|
bracket (SPI.openFd (fromAbs dest) SPI.WriteOnly (Just fm)
|
||||||
(SPI.defaultFileFlags { exclusive = True }))
|
(SPI.defaultFileFlags { exclusive = True }))
|
||||||
SPI.closeFd
|
SPI.closeFd
|
||||||
(\_ -> return ())
|
(\_ -> return ())
|
||||||
@@ -622,10 +675,24 @@ createRegularFile dest =
|
|||||||
--
|
--
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
-- - `PermissionDenied` if output directory cannot be written to
|
||||||
-- - `AlreadyExists` if destination directory already exists
|
-- - `AlreadyExists` if destination directory already exists
|
||||||
createDir :: Path Abs -> IO ()
|
createDir :: FileMode -> Path Abs -> IO ()
|
||||||
createDir dest = createDirectory (fromAbs dest) newDirPerms
|
createDir fm dest = createDirectory (fromAbs dest) fm
|
||||||
|
|
||||||
|
|
||||||
|
-- |Create a symlink.
|
||||||
|
--
|
||||||
|
-- Throws:
|
||||||
|
--
|
||||||
|
-- - `PermissionDenied` if output directory cannot be written to
|
||||||
|
-- - `AlreadyExists` if destination file already exists
|
||||||
|
--
|
||||||
|
-- Note: calls `symlink`
|
||||||
|
createSymlink :: Path Abs -- ^ destination file
|
||||||
|
-> ByteString -- ^ path the symlink points to
|
||||||
|
-> IO ()
|
||||||
|
createSymlink dest sympoint
|
||||||
|
= createSymbolicLink sympoint (fromAbs dest)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----------------------------
|
----------------------------
|
||||||
@@ -647,10 +714,14 @@ createDir dest = createDirectory (fromAbs dest) newDirPerms
|
|||||||
-- - `NoSuchThing` if source file does not exist
|
-- - `NoSuchThing` if source file does not exist
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
-- - `PermissionDenied` if output directory cannot be written to
|
||||||
-- - `PermissionDenied` if source directory cannot be opened
|
-- - `PermissionDenied` if source directory cannot be opened
|
||||||
-- - `UnsupportedOperation` if source and destination are on different devices
|
-- - `UnsupportedOperation` if source and destination are on different
|
||||||
-- - `FileDoesExist` if destination file already exists (`HPathIOException`)
|
-- devices
|
||||||
-- - `DirDoesExist` if destination directory already exists (`HPathIOException`)
|
-- - `FileDoesExist` if destination file already exists
|
||||||
-- - `SameFile` if destination and source are the same file (`HPathIOException`)
|
-- (`HPathIOException`)
|
||||||
|
-- - `DirDoesExist` if destination directory already exists
|
||||||
|
-- (`HPathIOException`)
|
||||||
|
-- - `SameFile` if destination and source are the same file
|
||||||
|
-- (`HPathIOException`)
|
||||||
--
|
--
|
||||||
-- Note: calls `rename` (but does not allow to rename over existing files)
|
-- Note: calls `rename` (but does not allow to rename over existing files)
|
||||||
renameFile :: Path Abs -> Path Abs -> IO ()
|
renameFile :: Path Abs -> Path Abs -> IO ()
|
||||||
@@ -666,66 +737,59 @@ renameFile fromf tof = do
|
|||||||
--
|
--
|
||||||
-- Does not follow symbolic links, but renames the symbolic link file.
|
-- Does not follow symbolic links, but renames the symbolic link file.
|
||||||
--
|
--
|
||||||
|
--
|
||||||
-- Safety/reliability concerns:
|
-- Safety/reliability concerns:
|
||||||
--
|
--
|
||||||
|
-- * `Overwrite` mode is not atomic
|
||||||
-- * copy-delete fallback is inherently non-atomic
|
-- * copy-delete fallback is inherently non-atomic
|
||||||
|
-- * since this function calls `easyCopy` and `easyDelete` as a fallback
|
||||||
|
-- to `renameFile`, file types that are not `RegularFile`, `SymbolicLink`
|
||||||
|
-- or `Directory` may be ignored
|
||||||
|
-- * for `Overwrite` mode, the destination will be deleted (not recursively)
|
||||||
|
-- before moving
|
||||||
--
|
--
|
||||||
-- Throws:
|
-- Throws:
|
||||||
--
|
--
|
||||||
-- - `NoSuchThing` if source file does not exist
|
-- - `NoSuchThing` if source file does not exist
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
-- - `PermissionDenied` if output directory cannot be written to
|
||||||
-- - `PermissionDenied` if source directory cannot be opened
|
-- - `PermissionDenied` if source directory cannot be opened
|
||||||
-- - `FileDoesExist` if destination file already exists (`HPathIOException`)
|
-- - `SameFile` if destination and source are the same file
|
||||||
-- - `DirDoesExist` if destination directory already exists (`HPathIOException`)
|
-- (`HPathIOException`)
|
||||||
-- - `SameFile` if destination and source are the same file (`HPathIOException`)
|
--
|
||||||
|
-- Throws in `Strict` mode only:
|
||||||
|
--
|
||||||
|
-- - `FileDoesExist` if destination file already exists (`HPathIOException`)
|
||||||
|
-- - `DirDoesExist` if destination directory already exists
|
||||||
|
-- (`HPathIOException`)
|
||||||
--
|
--
|
||||||
-- Note: calls `rename` (but does not allow to rename over existing files)
|
-- Note: calls `rename` (but does not allow to rename over existing files)
|
||||||
moveFile :: Path Abs -- ^ file to move
|
moveFile :: Path Abs -- ^ file to move
|
||||||
-> Path Abs -- ^ destination
|
-> Path Abs -- ^ destination
|
||||||
|
-> CopyMode
|
||||||
-> IO ()
|
-> IO ()
|
||||||
moveFile from to = do
|
moveFile from to cm = do
|
||||||
throwSameFile from to
|
throwSameFile from to
|
||||||
catchErrno [eXDEV] (renameFile from to) $ do
|
case cm of
|
||||||
easyCopy from to
|
Strict -> catchErrno [eXDEV] (renameFile from to) $ do
|
||||||
easyDelete from
|
easyCopy from to Strict FailEarly
|
||||||
|
easyDelete from
|
||||||
|
Overwrite -> do
|
||||||
|
ft <- getFileType from
|
||||||
|
writable <- isWritable $ dirname to
|
||||||
|
case ft of
|
||||||
|
RegularFile -> do
|
||||||
|
exists <- doesFileExist to
|
||||||
|
when (exists && writable) (deleteFile to)
|
||||||
|
SymbolicLink -> do
|
||||||
|
exists <- doesFileExist to
|
||||||
|
when (exists && writable) (deleteFile to)
|
||||||
|
Directory -> do
|
||||||
|
exists <- doesDirectoryExist to
|
||||||
|
when (exists && writable) (deleteDir to)
|
||||||
|
_ -> return ()
|
||||||
|
moveFile from to Strict
|
||||||
|
|
||||||
|
|
||||||
-- |Like `moveFile`, but overwrites the destination if it exists.
|
|
||||||
--
|
|
||||||
-- Does not follow symbolic links, but renames the symbolic link file.
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * copy-delete fallback is inherently non-atomic
|
|
||||||
-- * checks for file types and destination file existence explicitly
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if source file does not exist
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
|
||||||
-- - `PermissionDenied` if source directory cannot be opened
|
|
||||||
-- - `SameFile` if destination and source are the same file (`HPathIOException`)
|
|
||||||
--
|
|
||||||
-- Note: calls `rename` (but does not allow to rename over existing files)
|
|
||||||
moveFileOverwrite :: Path Abs -- ^ file to move
|
|
||||||
-> Path Abs -- ^ destination
|
|
||||||
-> IO ()
|
|
||||||
moveFileOverwrite from to = do
|
|
||||||
throwSameFile from to
|
|
||||||
ft <- getFileType from
|
|
||||||
writable <- isWritable $ dirname to
|
|
||||||
case ft of
|
|
||||||
RegularFile -> do
|
|
||||||
exists <- doesFileExist to
|
|
||||||
when (exists && writable) (deleteFile to)
|
|
||||||
SymbolicLink -> do
|
|
||||||
exists <- doesFileExist to
|
|
||||||
when (exists && writable) (deleteFile to)
|
|
||||||
Directory -> do
|
|
||||||
exists <- doesDirectoryExist to
|
|
||||||
when (exists && writable) (deleteDir to)
|
|
||||||
_ -> return ()
|
|
||||||
moveFile from to
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -765,6 +829,8 @@ newDirPerms
|
|||||||
-- |Gets all filenames of the given directory. This excludes "." and "..".
|
-- |Gets all filenames of the given directory. This excludes "." and "..".
|
||||||
-- This version does not follow symbolic links.
|
-- This version does not follow symbolic links.
|
||||||
--
|
--
|
||||||
|
-- The contents are not sorted and there is no guarantee on the ordering.
|
||||||
|
--
|
||||||
-- Throws:
|
-- Throws:
|
||||||
--
|
--
|
||||||
-- - `NoSuchThing` if directory does not exist
|
-- - `NoSuchThing` if directory does not exist
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- |
|
-- |
|
||||||
-- Module : HPath.IO.Errors
|
-- Module : HPath.IO.Errors
|
||||||
-- Copyright : © 2016 Julian Ospald
|
-- Copyright : © 2016 Julian Ospald
|
||||||
-- License : GPL-2
|
-- License : BSD3
|
||||||
--
|
--
|
||||||
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
||||||
-- Stability : experimental
|
-- Stability : experimental
|
||||||
@@ -27,6 +27,7 @@ module HPath.IO.Errors
|
|||||||
, isInvalidOperation
|
, isInvalidOperation
|
||||||
, isCan'tOpenDirectory
|
, isCan'tOpenDirectory
|
||||||
, isCopyFailed
|
, isCopyFailed
|
||||||
|
, isRecursiveFailure
|
||||||
|
|
||||||
-- * Path based functions
|
-- * Path based functions
|
||||||
, throwFileDoesExist
|
, throwFileDoesExist
|
||||||
@@ -70,10 +71,6 @@ import Data.ByteString.UTF8
|
|||||||
(
|
(
|
||||||
toString
|
toString
|
||||||
)
|
)
|
||||||
import Data.Data
|
|
||||||
(
|
|
||||||
Data(..)
|
|
||||||
)
|
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import Foreign.C.Error
|
import Foreign.C.Error
|
||||||
(
|
(
|
||||||
@@ -114,7 +111,8 @@ data HPathIOException = FileDoesNotExist ByteString
|
|||||||
| InvalidOperation String
|
| InvalidOperation String
|
||||||
| Can'tOpenDirectory ByteString
|
| Can'tOpenDirectory ByteString
|
||||||
| CopyFailed String
|
| CopyFailed String
|
||||||
deriving (Typeable, Eq, Data)
|
| RecursiveFailure [IOException]
|
||||||
|
deriving (Typeable, Eq)
|
||||||
|
|
||||||
|
|
||||||
instance Show HPathIOException where
|
instance Show HPathIOException where
|
||||||
@@ -133,6 +131,22 @@ instance Show HPathIOException where
|
|||||||
show (Can'tOpenDirectory fp) = "Can't open directory: "
|
show (Can'tOpenDirectory fp) = "Can't open directory: "
|
||||||
++ toString fp
|
++ toString fp
|
||||||
show (CopyFailed str) = "Copying failed: " ++ str
|
show (CopyFailed str) = "Copying failed: " ++ str
|
||||||
|
show (RecursiveFailure exs) = "Recursive operation failed: "
|
||||||
|
++ show exs
|
||||||
|
|
||||||
|
|
||||||
|
toConstr :: HPathIOException -> String
|
||||||
|
toConstr FileDoesNotExist {} = "FileDoesNotExist"
|
||||||
|
toConstr DirDoesNotExist {} = "DirDoesNotExist"
|
||||||
|
toConstr SameFile {} = "SameFile"
|
||||||
|
toConstr DestinationInSource {} = "DestinationInSource"
|
||||||
|
toConstr FileDoesExist {} = "FileDoesExist"
|
||||||
|
toConstr DirDoesExist {} = "DirDoesExist"
|
||||||
|
toConstr InvalidOperation {} = "InvalidOperation"
|
||||||
|
toConstr Can'tOpenDirectory {} = "Can'tOpenDirectory"
|
||||||
|
toConstr CopyFailed {} = "CopyFailed"
|
||||||
|
toConstr RecursiveFailure {} = "RecursiveFailure"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -146,7 +160,7 @@ instance Exception HPathIOException
|
|||||||
--[ Exception identifiers ]--
|
--[ Exception identifiers ]--
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
isFileDoesNotExist, isDirDoesNotExist, isSameFile, isDestinationInSource, isFileDoesExist, isDirDoesExist, isInvalidOperation, isCan'tOpenDirectory, isCopyFailed :: HPathIOException -> Bool
|
isFileDoesNotExist, isDirDoesNotExist, isSameFile, isDestinationInSource, isFileDoesExist, isDirDoesExist, isInvalidOperation, isCan'tOpenDirectory, isCopyFailed, isRecursiveFailure :: HPathIOException -> Bool
|
||||||
isFileDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr FileDoesNotExist{}
|
isFileDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr FileDoesNotExist{}
|
||||||
isDirDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesNotExist{}
|
isDirDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesNotExist{}
|
||||||
isSameFile ex = toConstr (ex :: HPathIOException) == toConstr SameFile{}
|
isSameFile ex = toConstr (ex :: HPathIOException) == toConstr SameFile{}
|
||||||
@@ -156,7 +170,7 @@ isDirDoesExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesExist{}
|
|||||||
isInvalidOperation ex = toConstr (ex :: HPathIOException) == toConstr InvalidOperation{}
|
isInvalidOperation ex = toConstr (ex :: HPathIOException) == toConstr InvalidOperation{}
|
||||||
isCan'tOpenDirectory ex = toConstr (ex :: HPathIOException) == toConstr Can'tOpenDirectory{}
|
isCan'tOpenDirectory ex = toConstr (ex :: HPathIOException) == toConstr Can'tOpenDirectory{}
|
||||||
isCopyFailed ex = toConstr (ex :: HPathIOException) == toConstr CopyFailed{}
|
isCopyFailed ex = toConstr (ex :: HPathIOException) == toConstr CopyFailed{}
|
||||||
|
isRecursiveFailure ex = toConstr (ex :: HPathIOException) == toConstr RecursiveFailure{}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- |
|
-- |
|
||||||
-- Module : HPath.IO.Utils
|
-- Module : HPath.IO.Utils
|
||||||
-- Copyright : © 2016 Julian Ospald
|
-- Copyright : © 2016 Julian Ospald
|
||||||
-- License : GPL-2
|
-- License : BSD3
|
||||||
--
|
--
|
||||||
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
||||||
-- Stability : experimental
|
-- Stability : experimental
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
-- | Internal types and functions.
|
-- | Internal types and functions.
|
||||||
|
|
||||||
module HPath.Internal
|
module HPath.Internal
|
||||||
(Path(..)
|
(Path(..))
|
||||||
,RelC)
|
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.DeepSeq (NFData (..))
|
import Control.DeepSeq (NFData (..))
|
||||||
@@ -13,7 +12,7 @@ import Data.Data
|
|||||||
|
|
||||||
-- | Path of some base and type.
|
-- | Path of some base and type.
|
||||||
--
|
--
|
||||||
-- Internally is a string. The string can be of two formats only:
|
-- Internally is a ByteString. The ByteString can be of two formats only:
|
||||||
--
|
--
|
||||||
-- 1. without trailing path separator: @file.txt@, @foo\/bar.txt@, @\/foo\/bar.txt@
|
-- 1. without trailing path separator: @file.txt@, @foo\/bar.txt@, @\/foo\/bar.txt@
|
||||||
-- 2. with trailing path separator: @foo\/@, @\/foo\/bar\/@
|
-- 2. with trailing path separator: @foo\/@, @\/foo\/bar\/@
|
||||||
@@ -23,7 +22,7 @@ import Data.Data
|
|||||||
data Path b = MkPath ByteString
|
data Path b = MkPath ByteString
|
||||||
deriving (Typeable)
|
deriving (Typeable)
|
||||||
|
|
||||||
-- | String equality.
|
-- | ByteString equality.
|
||||||
--
|
--
|
||||||
-- The following property holds:
|
-- The following property holds:
|
||||||
--
|
--
|
||||||
@@ -31,7 +30,7 @@ data Path b = MkPath ByteString
|
|||||||
instance Eq (Path b) where
|
instance Eq (Path b) where
|
||||||
(==) (MkPath x) (MkPath y) = x == y
|
(==) (MkPath x) (MkPath y) = x == y
|
||||||
|
|
||||||
-- | String ordering.
|
-- | ByteString ordering.
|
||||||
--
|
--
|
||||||
-- The following property holds:
|
-- The following property holds:
|
||||||
--
|
--
|
||||||
@@ -39,7 +38,7 @@ instance Eq (Path b) where
|
|||||||
instance Ord (Path b) where
|
instance Ord (Path b) where
|
||||||
compare (MkPath x) (MkPath y) = compare x y
|
compare (MkPath x) (MkPath y) = compare x y
|
||||||
|
|
||||||
-- | Same as 'Path.toFilePath'.
|
-- | Same as 'HPath.toFilePath'.
|
||||||
--
|
--
|
||||||
-- The following property holds:
|
-- The following property holds:
|
||||||
--
|
--
|
||||||
@@ -50,6 +49,3 @@ instance Show (Path b) where
|
|||||||
instance NFData (Path b) where
|
instance NFData (Path b) where
|
||||||
rnf (MkPath x) = rnf x
|
rnf (MkPath x) = rnf x
|
||||||
|
|
||||||
|
|
||||||
class RelC m
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
-- Traversal and read operations on directories.
|
-- Traversal and read operations on directories.
|
||||||
|
|
||||||
|
|
||||||
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE PackageImports #-}
|
{-# LANGUAGE PackageImports #-}
|
||||||
@@ -37,7 +38,10 @@ module System.Posix.Directory.Traversals (
|
|||||||
, realpath
|
, realpath
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Applicative
|
|
||||||
|
#if __GLASGOW_HASKELL__ < 710
|
||||||
|
import Control.Applicative ((<$>))
|
||||||
|
#endif
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import System.Posix.FilePath ((</>))
|
import System.Posix.FilePath ((</>))
|
||||||
import System.Posix.Directory.Foreign
|
import System.Posix.Directory.Foreign
|
||||||
|
|||||||
@@ -13,54 +13,66 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
|
||||||
specDir = "test/HPath/IO/canonicalizePathSpec/"
|
|
||||||
|
|
||||||
specDir' :: String
|
upTmpDir :: IO ()
|
||||||
specDir' = toString specDir
|
upTmpDir = do
|
||||||
|
setTmpDir "CanonicalizePathSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "file"
|
||||||
|
createDir' "dir"
|
||||||
|
createSymlink' "dirSym" "dir/"
|
||||||
|
createSymlink' "brokenSym" "nothing"
|
||||||
|
createSymlink' "fileSym" "file"
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
deleteFile' "file"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteFile' "dirSym"
|
||||||
|
deleteFile' "brokenSym"
|
||||||
|
deleteFile' "fileSym"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.canonicalizePath" $ do
|
describe "HPath.IO.canonicalizePath" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "canonicalizePath, all fine" $ do
|
it "canonicalizePath, all fine" $ do
|
||||||
path <- withPwd (specDir `ba` "file") return
|
path <- withTmpDir "file" return
|
||||||
canonicalizePath' (specDir `ba` "file")
|
canonicalizePath' "file"
|
||||||
`shouldReturn` path
|
`shouldReturn` path
|
||||||
|
|
||||||
it "canonicalizePath, all fine" $ do
|
it "canonicalizePath, all fine" $ do
|
||||||
path <- withPwd (specDir `ba` "dir") return
|
path <- withTmpDir "dir" return
|
||||||
canonicalizePath' (specDir `ba` "dir")
|
canonicalizePath' "dir"
|
||||||
`shouldReturn` path
|
`shouldReturn` path
|
||||||
|
|
||||||
it "canonicalizePath, all fine" $ do
|
it "canonicalizePath, all fine" $ do
|
||||||
path <- withPwd (specDir `ba` "file") return
|
path <- withTmpDir "file" return
|
||||||
canonicalizePath' (specDir `ba` "fileSym")
|
canonicalizePath' "fileSym"
|
||||||
`shouldReturn` path
|
`shouldReturn` path
|
||||||
|
|
||||||
it "canonicalizePath, all fine" $ do
|
it "canonicalizePath, all fine" $ do
|
||||||
path <- withPwd (specDir `ba` "dir") return
|
path <- withTmpDir "dir" return
|
||||||
canonicalizePath' (specDir `ba` "dirSym")
|
canonicalizePath' "dirSym"
|
||||||
`shouldReturn` path
|
`shouldReturn` path
|
||||||
|
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "canonicalizePath, broken symlink" $
|
it "canonicalizePath, broken symlink" $
|
||||||
canonicalizePath' (specDir `ba` "brokenSym")
|
canonicalizePath' "brokenSym"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "canonicalizePath, file does not exist" $
|
it "canonicalizePath, file does not exist" $
|
||||||
canonicalizePath' (specDir `ba` "nothingBlah")
|
canonicalizePath' "nothingBlah"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
|
|||||||
245
test/HPath/IO/CopyDirRecursiveCollectFailuresSpec.hs
Normal file
245
test/HPath/IO/CopyDirRecursiveCollectFailuresSpec.hs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
{-# 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 (\e -> ioeGetErrorType e == InappropriateType) ex &&
|
||||||
|
any (\e -> ioeGetErrorType e == PermissionDenied) 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 [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 [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 [e]) -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
|
it "copyDirRecursive (Strict, CollectFailures), wrong input (symlink to directory)" $
|
||||||
|
copyDirRecursive' "wrongInputSymL"
|
||||||
|
"outputDir"
|
||||||
|
Strict
|
||||||
|
CollectFailures
|
||||||
|
`shouldThrow`
|
||||||
|
(\(RecursiveFailure [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
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module HPath.IO.CopyDirRecursiveOverwriteSpec where
|
module HPath.IO.CopyDirRecursiveOverwriteSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import HPath.IO
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@@ -16,95 +18,185 @@ import GHC.IO.Exception
|
|||||||
import System.Exit
|
import System.Exit
|
||||||
import System.Process
|
import System.Process
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
import Data.ByteString.UTF8 (toString)
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/copyDirRecursiveOverwriteSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "CopyDirRecursiveOverwriteSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "alreadyExists"
|
||||||
|
createRegularFile' "wrongInput"
|
||||||
|
createSymlink' "wrongInputSymL" "inputDir/"
|
||||||
|
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' "alreadyExistsD"
|
||||||
|
createDir' "alreadyExistsD/bar"
|
||||||
|
createDir' "alreadyExistsD/foo"
|
||||||
|
createRegularFile' "alreadyExistsD/foo/inputFile1"
|
||||||
|
createRegularFile' "alreadyExistsD/inputFile2"
|
||||||
|
createRegularFile' "alreadyExistsD/bar/inputFile3"
|
||||||
|
writeFile' "alreadyExistsD/foo/inputFile1" "DAAsada"
|
||||||
|
writeFile' "alreadyExistsD/inputFile2" "ahfaagaga"
|
||||||
|
writeFile' "alreadyExistsD/bar/inputFile3"
|
||||||
|
"f3223sasdasdaasdasdasasd4"
|
||||||
|
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerm"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerm"
|
||||||
|
deleteFile' "alreadyExists"
|
||||||
|
deleteFile' "wrongInput"
|
||||||
|
deleteFile' "wrongInputSymL"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "noWritePerm"
|
||||||
|
deleteFile' "inputDir/foo/inputFile1"
|
||||||
|
deleteFile' "inputDir/inputFile2"
|
||||||
|
deleteFile' "inputDir/bar/inputFile3"
|
||||||
|
deleteDir' "inputDir/foo"
|
||||||
|
deleteDir' "inputDir/bar"
|
||||||
|
deleteDir' "inputDir"
|
||||||
|
deleteFile' "alreadyExistsD/foo/inputFile1"
|
||||||
|
deleteFile' "alreadyExistsD/inputFile2"
|
||||||
|
deleteFile' "alreadyExistsD/bar/inputFile3"
|
||||||
|
deleteDir' "alreadyExistsD/foo"
|
||||||
|
deleteDir' "alreadyExistsD/bar"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.copyDirRecursiveOverwrite" $ do
|
describe "HPath.IO.copyDirRecursive" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyDirRecursiveOverwrite, all fine" $ do
|
it "copyDirRecursive (Overwrite, FailEarly), all fine" $ do
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "outputDir")
|
"outputDir"
|
||||||
removeDirIfExists $ specDir `ba` "outputDir"
|
Overwrite
|
||||||
|
FailEarly
|
||||||
|
removeDirIfExists "outputDir"
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, all fine and compare" $ do
|
it "copyDirRecursive (Overwrite, FailEarly), all fine and compare" $ do
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "inputDir")
|
tmpDir' <- getRawTmpDir
|
||||||
(specDir `ba` "outputDir")
|
copyDirRecursive' "inputDir"
|
||||||
|
"outputDir"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
(system $ "diff -r --no-dereference "
|
(system $ "diff -r --no-dereference "
|
||||||
++ specDir' ++ "inputDir" ++ " "
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
++ specDir' ++ "outputDir")
|
++ toString tmpDir' ++ "outputDir")
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeDirIfExists $ specDir `ba` "outputDir"
|
removeDirIfExists "outputDir"
|
||||||
|
|
||||||
|
it "copyDirRecursive (Overwrite, FailEarly), destination dir already exists" $ do
|
||||||
|
tmpDir' <- getRawTmpDir
|
||||||
|
(system $ "diff -r --no-dereference "
|
||||||
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
|
++ toString tmpDir' ++ "alreadyExistsD")
|
||||||
|
`shouldReturn` (ExitFailure 1)
|
||||||
|
copyDirRecursive' "inputDir"
|
||||||
|
"alreadyExistsD"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
|
(system $ "diff -r --no-dereference "
|
||||||
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
|
++ toString tmpDir' ++ "alreadyExistsD")
|
||||||
|
`shouldReturn` ExitSuccess
|
||||||
|
removeDirIfExists "outputDir"
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, destination dir already exists" $
|
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "inputDir")
|
|
||||||
(specDir `ba` "alreadyExistsD")
|
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "copyDirRecursiveOverwrite, source directory does not exist" $
|
it "copyDirRecursive, source directory does not exist" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "doesNotExist")
|
copyDirRecursive' "doesNotExist"
|
||||||
(specDir `ba` "outputDir")
|
"outputDir"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, no write permission on output dir" $
|
it "copyDirRecursive, no write permission on output dir" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "noWritePerm/foo")
|
"noWritePerm/foo"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, cannot open output dir" $
|
it "copyDirRecursive, cannot open output dir" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "noPerms/foo")
|
"noPerms/foo"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, cannot open source dir" $
|
it "copyDirRecursive, cannot open source dir" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "noPerms/inputDir")
|
copyDirRecursive' "noPerms/inputDir"
|
||||||
(specDir `ba` "foo")
|
"foo"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, destination already exists and is a file" $
|
it "copyDirRecursive, destination already exists and is a file" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "alreadyExists")
|
"alreadyExists"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, wrong input (regular file)" $
|
it "copyDirRecursive, wrong input (regular file)" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "wrongInput")
|
copyDirRecursive' "wrongInput"
|
||||||
(specDir `ba` "outputDir")
|
"outputDir"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, wrong input (symlink to directory)" $
|
it "copyDirRecursive, wrong input (symlink to directory)" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "wrongInputSymL")
|
copyDirRecursive' "wrongInputSymL"
|
||||||
(specDir `ba` "outputDir")
|
"outputDir"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||||
|
|
||||||
-- custom failures
|
-- custom failures
|
||||||
it "copyDirRecursiveOverwrite, destination in source" $
|
it "copyDirRecursive (Overwrite, FailEarly), destination in source" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "inputDir/foo")
|
"inputDir/foo"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isDestinationInSource
|
isDestinationInSource
|
||||||
|
|
||||||
it "copyDirRecursiveOverwrite, destination and source same directory" $
|
it "copyDirRecursive (Overwrite, FailEarly), destination and source same directory" $
|
||||||
copyDirRecursiveOverwrite' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "inputDir")
|
"inputDir"
|
||||||
|
Overwrite
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isSameFile
|
isSameFile
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module HPath.IO.CopyDirRecursiveSpec where
|
module HPath.IO.CopyDirRecursiveSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import HPath.IO
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@@ -16,97 +18,163 @@ import GHC.IO.Exception
|
|||||||
import System.Exit
|
import System.Exit
|
||||||
import System.Process
|
import System.Process
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
import Data.ByteString.UTF8 (toString)
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/copyDirRecursiveSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "CopyDirRecursiveSpec"
|
||||||
|
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"
|
||||||
|
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerm"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerm"
|
||||||
|
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"
|
||||||
|
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.copyDirRecursive" $ do
|
describe "HPath.IO.copyDirRecursive" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyDirRecursive, all fine" $ do
|
it "copyDirRecursive (Strict, FailEarly), all fine" $ do
|
||||||
copyDirRecursive' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "outputDir")
|
"outputDir"
|
||||||
removeDirIfExists (specDir `ba` "outputDir")
|
Strict
|
||||||
|
FailEarly
|
||||||
|
removeDirIfExists "outputDir"
|
||||||
|
|
||||||
it "copyDirRecursive, all fine and compare" $ do
|
it "copyDirRecursive (Strict, FailEarly), all fine and compare" $ do
|
||||||
copyDirRecursive' (specDir `ba` "inputDir")
|
tmpDir' <- getRawTmpDir
|
||||||
(specDir `ba` "outputDir")
|
copyDirRecursive' "inputDir"
|
||||||
|
"outputDir"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
(system $ "diff -r --no-dereference "
|
(system $ "diff -r --no-dereference "
|
||||||
++ specDir' ++ "inputDir" ++ " "
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
++ specDir' ++ "outputDir")
|
++ toString tmpDir' ++ "outputDir")
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeDirIfExists (specDir `ba` "outputDir")
|
removeDirIfExists "outputDir"
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "copyDirRecursive, source directory does not exist" $
|
it "copyDirRecursive (Strict, FailEarly), source directory does not exist" $
|
||||||
copyDirRecursive' (specDir `ba` "doesNotExist")
|
copyDirRecursive' "doesNotExist"
|
||||||
(specDir `ba` "outputDir")
|
"outputDir"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "copyDirRecursive, no write permission on output dir" $
|
it "copyDirRecursive (Strict, FailEarly), no write permission on output dir" $
|
||||||
copyDirRecursive' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "noWritePerm/foo")
|
"noWritePerm/foo"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyDirRecursive, cannot open output dir" $
|
it "copyDirRecursive (Strict, FailEarly), cannot open output dir" $
|
||||||
copyDirRecursive' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "noPerms/foo")
|
"noPerms/foo"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyDirRecursive, cannot open source dir" $
|
it "copyDirRecursive (Strict, FailEarly), cannot open source dir" $
|
||||||
copyDirRecursive' (specDir `ba` "noPerms/inputDir")
|
copyDirRecursive' "noPerms/inputDir"
|
||||||
(specDir `ba` "foo")
|
"foo"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyDirRecursive, destination dir already exists" $
|
it "copyDirRecursive (Strict, FailEarly), destination dir already exists" $
|
||||||
copyDirRecursive' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "alreadyExistsD")
|
"alreadyExistsD"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
(\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' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "alreadyExists")
|
"alreadyExists"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||||
|
|
||||||
it "copyDirRecursive, wrong input (regular file)" $
|
it "copyDirRecursive (Strict, FailEarly), wrong input (regular file)" $
|
||||||
copyDirRecursive' (specDir `ba` "wrongInput")
|
copyDirRecursive' "wrongInput"
|
||||||
(specDir `ba` "outputDir")
|
"outputDir"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "copyDirRecursive, wrong input (symlink to directory)" $
|
it "copyDirRecursive (Strict, FailEarly), wrong input (symlink to directory)" $
|
||||||
copyDirRecursive' (specDir `ba` "wrongInputSymL")
|
copyDirRecursive' "wrongInputSymL"
|
||||||
(specDir `ba` "outputDir")
|
"outputDir"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||||
|
|
||||||
-- custom failures
|
-- custom failures
|
||||||
it "copyDirRecursive, destination in source" $
|
it "copyDirRecursive (Strict, FailEarly), destination in source" $
|
||||||
copyDirRecursive' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "inputDir/foo")
|
"inputDir/foo"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isDestinationInSource
|
isDestinationInSource
|
||||||
|
|
||||||
it "copyDirRecursive, destination and source same directory" $
|
it "copyDirRecursive (Strict, FailEarly), destination and source same directory" $
|
||||||
copyDirRecursive' (specDir `ba` "inputDir")
|
copyDirRecursive' "inputDir"
|
||||||
(specDir `ba` "inputDir")
|
"inputDir"
|
||||||
|
Strict
|
||||||
|
FailEarly
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isSameFile
|
isSameFile
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module HPath.IO.CopyFileOverwriteSpec where
|
|||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import HPath.IO
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@@ -16,94 +17,132 @@ import GHC.IO.Exception
|
|||||||
import System.Exit
|
import System.Exit
|
||||||
import System.Process
|
import System.Process
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
import Data.ByteString.UTF8 (toString)
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/copyFileOverwriteSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "CopyFileOverwriteSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "inputFile"
|
||||||
|
createRegularFile' "alreadyExists"
|
||||||
|
createSymlink' "inputFileSymL" "inputFile"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createRegularFile' "noPerms/inputFile"
|
||||||
|
createDir' "outputDirNoWrite"
|
||||||
|
createDir' "wrongInput"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "outputDirNoWrite"
|
||||||
|
writeFile' "inputFile" "Blahfaselgagaga"
|
||||||
|
writeFile' "alreadyExists" "dsaldsalkaklsdlkasksdadasl"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "outputDirNoWrite"
|
||||||
|
deleteFile' "noPerms/inputFile"
|
||||||
|
deleteFile' "inputFile"
|
||||||
|
deleteFile' "alreadyExists"
|
||||||
|
deleteFile' "inputFileSymL"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "outputDirNoWrite"
|
||||||
|
deleteDir' "wrongInput"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.copyFileOverwrite" $ do
|
describe "HPath.IO.copyFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyFileOverwrite, everything clear" $ do
|
it "copyFile (Overwrite), everything clear" $ do
|
||||||
copyFileOverwrite' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
removeFileIfExists (specDir `ba` "outputFile")
|
Overwrite
|
||||||
|
removeFileIfExists "outputFile"
|
||||||
|
|
||||||
it "copyFileOverwrite, output file already exists, all clear" $ do
|
it "copyFile (Overwrite), output file already exists, all clear" $ do
|
||||||
copyFile' (specDir `ba` "alreadyExists") (specDir `ba` "alreadyExists.bak")
|
tmpDir' <- getRawTmpDir
|
||||||
copyFileOverwrite' (specDir `ba` "inputFile")
|
copyFile' "alreadyExists" "alreadyExists.bak" Strict
|
||||||
(specDir `ba` "alreadyExists")
|
copyFile' "inputFile" "alreadyExists" Overwrite
|
||||||
(system $ "cmp -s " ++ specDir' ++ "inputFile" ++ " "
|
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||||
++ specDir' ++ "alreadyExists")
|
++ toString tmpDir' ++ "alreadyExists")
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeFileIfExists (specDir `ba` "alreadyExists")
|
removeFileIfExists "alreadyExists"
|
||||||
copyFile' (specDir `ba` "alreadyExists.bak") (specDir `ba` "alreadyExists")
|
copyFile' "alreadyExists.bak" "alreadyExists" Strict
|
||||||
removeFileIfExists (specDir `ba` "alreadyExists.bak")
|
removeFileIfExists "alreadyExists.bak"
|
||||||
|
|
||||||
it "copyFileOverwrite, and compare" $ do
|
it "copyFile (Overwrite), and compare" $ do
|
||||||
copyFileOverwrite' (specDir `ba` "inputFile")
|
tmpDir' <- getRawTmpDir
|
||||||
(specDir `ba` "outputFile")
|
copyFile' "inputFile"
|
||||||
(system $ "cmp -s " ++ specDir' ++ "inputFile" ++ " "
|
"outputFile"
|
||||||
++ specDir' ++ "outputFile")
|
Overwrite
|
||||||
|
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||||
|
++ toString tmpDir' ++ "outputFile")
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeFileIfExists (specDir `ba` "outputFile")
|
removeFileIfExists "outputFile"
|
||||||
|
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "copyFileOverwrite, input file does not exist" $
|
it "copyFile (Overwrite), input file does not exist" $
|
||||||
copyFileOverwrite' (specDir `ba` "noSuchFile")
|
copyFile' "noSuchFile"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "copyFileOverwrite, no permission to write to output directory" $
|
it "copyFile (Overwrite), no permission to write to output directory" $
|
||||||
copyFileOverwrite' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "outputDirNoWrite/outputFile")
|
"outputDirNoWrite/outputFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyFileOverwrite, cannot open output directory" $
|
it "copyFile (Overwrite), cannot open output directory" $
|
||||||
copyFileOverwrite' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "noPerms/outputFile")
|
"noPerms/outputFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyFileOverwrite, cannot open source directory" $
|
it "copyFile (Overwrite), cannot open source directory" $
|
||||||
copyFileOverwrite' (specDir `ba` "noPerms/inputFile")
|
copyFile' "noPerms/inputFile"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyFileOverwrite, wrong input type (symlink)" $
|
it "copyFile (Overwrite), wrong input type (symlink)" $
|
||||||
copyFileOverwrite' (specDir `ba` "inputFileSymL")
|
copyFile' "inputFileSymL"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||||
|
|
||||||
it "copyFileOverwrite, wrong input type (directory)" $
|
it "copyFile (Overwrite), wrong input type (directory)" $
|
||||||
copyFileOverwrite' (specDir `ba` "wrongInput")
|
copyFile' "wrongInput"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "copyFileOverwrite, output file already exists and is a dir" $
|
it "copyFile (Overwrite), output file already exists and is a dir" $
|
||||||
copyFileOverwrite' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "alreadyExistsD")
|
"alreadyExistsD"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
-- custom failures --
|
-- custom failures --
|
||||||
it "copyFileOverwrite, output and input are same file" $
|
it "copyFile (Overwrite), output and input are same file" $
|
||||||
copyFileOverwrite' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "inputFile")
|
"inputFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow` isSameFile
|
`shouldThrow` isSameFile
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module HPath.IO.CopyFileSpec where
|
module HPath.IO.CopyFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import HPath.IO
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@@ -16,90 +18,126 @@ import GHC.IO.Exception
|
|||||||
import System.Exit
|
import System.Exit
|
||||||
import System.Process
|
import System.Process
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
import Data.ByteString.UTF8 (toString)
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/copyFileSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "CopyFileSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir' :: String
|
setupFiles :: IO ()
|
||||||
specDir' = toString specDir
|
setupFiles = do
|
||||||
|
createRegularFile' "inputFile"
|
||||||
|
createRegularFile' "alreadyExists"
|
||||||
|
createSymlink' "inputFileSymL" "inputFile"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createRegularFile' "noPerms/inputFile"
|
||||||
|
createDir' "outputDirNoWrite"
|
||||||
|
createDir' "wrongInput"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "outputDirNoWrite"
|
||||||
|
writeFile' "inputFile" "Blahfaselgagaga"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "outputDirNoWrite"
|
||||||
|
deleteFile' "noPerms/inputFile"
|
||||||
|
deleteFile' "inputFile"
|
||||||
|
deleteFile' "alreadyExists"
|
||||||
|
deleteFile' "inputFileSymL"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "outputDirNoWrite"
|
||||||
|
deleteDir' "wrongInput"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.copyFile" $ do
|
describe "HPath.IO.copyFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyFile, everything clear" $ do
|
it "copyFile (Strict), everything clear" $ do
|
||||||
copyFile' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
removeFileIfExists (specDir `ba` "outputFile")
|
Strict
|
||||||
|
removeFileIfExists "outputFile"
|
||||||
|
|
||||||
it "copyFile, and compare" $ do
|
it "copyFile (Strict), and compare" $ do
|
||||||
copyFile' (specDir `ba` "inputFile")
|
tmpDir' <- getRawTmpDir
|
||||||
(specDir `ba` "outputFile")
|
copyFile' "inputFile"
|
||||||
(system $ "cmp -s " ++ specDir' ++ "inputFile" ++ " "
|
"outputFile"
|
||||||
++ specDir' ++ "outputFile")
|
Strict
|
||||||
|
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||||
|
++ toString tmpDir' ++ "outputFile")
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeFileIfExists (specDir `ba` "outputFile")
|
removeFileIfExists "outputFile"
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "copyFile, input file does not exist" $
|
it "copyFile (Strict), input file does not exist" $
|
||||||
copyFile' (specDir `ba` "noSuchFile")
|
copyFile' "noSuchFile"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "copyFile, no permission to write to output directory" $
|
it "copyFile (Strict), no permission to write to output directory" $
|
||||||
copyFile' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "outputDirNoWrite/outputFile")
|
"outputDirNoWrite/outputFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyFile, cannot open output directory" $
|
it "copyFile (Strict), cannot open output directory" $
|
||||||
copyFile' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "noPerms/outputFile")
|
"noPerms/outputFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyFile, cannot open source directory" $
|
it "copyFile (Strict), cannot open source directory" $
|
||||||
copyFile' (specDir `ba` "noPerms/inputFile")
|
copyFile' "noPerms/inputFile"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "copyFile, wrong input type (symlink)" $
|
it "copyFile (Strict), wrong input type (symlink)" $
|
||||||
copyFile' (specDir `ba` "inputFileSymL")
|
copyFile' "inputFileSymL"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||||
|
|
||||||
it "copyFile, wrong input type (directory)" $
|
it "copyFile (Strict), wrong input type (directory)" $
|
||||||
copyFile' (specDir `ba` "wrongInput")
|
copyFile' "wrongInput"
|
||||||
(specDir `ba` "outputFile")
|
"outputFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "copyFile, output file already exists" $
|
it "copyFile (Strict), output file already exists" $
|
||||||
copyFile' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "alreadyExists")
|
"alreadyExists"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
(\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' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "alreadyExistsD")
|
"alreadyExistsD"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||||
|
|
||||||
-- custom failures --
|
-- custom failures --
|
||||||
it "copyFile, output and input are same file" $
|
it "copyFile (Strict), output and input are same file" $
|
||||||
copyFile' (specDir `ba` "inputFile")
|
copyFile' "inputFile"
|
||||||
(specDir `ba` "inputFile")
|
"inputFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isSameFile
|
isSameFile
|
||||||
|
|||||||
@@ -13,42 +13,55 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/createDirSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "CreateDirSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir' :: String
|
setupFiles :: IO ()
|
||||||
specDir' = toString specDir
|
setupFiles = do
|
||||||
|
createDir' "alreadyExists"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createDir' "noWritePerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerms"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerms"
|
||||||
|
deleteDir' "alreadyExists"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "noWritePerms"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.createDir" $ do
|
describe "HPath.IO.createDir" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "createDir, all fine" $ do
|
it "createDir, all fine" $ do
|
||||||
createDir' (specDir `ba` "newDir")
|
createDir' "newDir"
|
||||||
removeDirIfExists (specDir `ba` "newDir")
|
removeDirIfExists "newDir"
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "createDir, can't write to output directory" $
|
it "createDir, can't write to output directory" $
|
||||||
createDir' (specDir `ba` "noWritePerms/newDir")
|
createDir' "noWritePerms/newDir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "createDir, can't open output directory" $
|
it "createDir, can't open output directory" $
|
||||||
createDir' (specDir `ba` "noPerms/newDir")
|
createDir' "noPerms/newDir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "createDir, destination directory already exists" $
|
it "createDir, destination directory already exists" $
|
||||||
createDir' (specDir `ba` "alreadyExists")
|
createDir' "alreadyExists"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||||
|
|
||||||
|
|||||||
@@ -13,42 +13,53 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/createRegularFileSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "CreateRegularFileSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir' :: String
|
setupFiles :: IO ()
|
||||||
specDir' = toString specDir
|
setupFiles = do
|
||||||
|
createRegularFile' "alreadyExists"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createDir' "noWritePerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerms"
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerms"
|
||||||
|
deleteFile' "alreadyExists"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "noWritePerms"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.createRegularFile" $ do
|
describe "HPath.IO.createRegularFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "createRegularFile, all fine" $ do
|
it "createRegularFile, all fine" $ do
|
||||||
createRegularFile' (specDir `ba` "newDir")
|
createRegularFile' "newDir"
|
||||||
removeFileIfExists (specDir `ba` "newDir")
|
removeFileIfExists "newDir"
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "createRegularFile, can't write to destination directory" $
|
it "createRegularFile, can't write to destination directory" $
|
||||||
createRegularFile' (specDir `ba` "noWritePerms/newDir")
|
createRegularFile' "noWritePerms/newDir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "createRegularFile, can't write to destination directory" $
|
it "createRegularFile, can't write to destination directory" $
|
||||||
createRegularFile' (specDir `ba` "noPerms/newDir")
|
createRegularFile' "noPerms/newDir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "createRegularFile, destination file already exists" $
|
it "createRegularFile, destination file already exists" $
|
||||||
createRegularFile' (specDir `ba` "alreadyExists")
|
createRegularFile' "alreadyExists"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||||
|
|
||||||
|
|||||||
66
test/HPath/IO/CreateSymlinkSpec.hs
Normal file
66
test/HPath/IO/CreateSymlinkSpec.hs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module HPath.IO.CreateSymlinkSpec where
|
||||||
|
|
||||||
|
|
||||||
|
import Test.Hspec
|
||||||
|
import System.IO.Error
|
||||||
|
(
|
||||||
|
ioeGetErrorType
|
||||||
|
)
|
||||||
|
import GHC.IO.Exception
|
||||||
|
(
|
||||||
|
IOErrorType(..)
|
||||||
|
)
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
|
upTmpDir :: IO ()
|
||||||
|
upTmpDir = do
|
||||||
|
setTmpDir "CreateSymlinkSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "alreadyExists"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createDir' "noWritePerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerms"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerms"
|
||||||
|
deleteFile' "alreadyExists"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "noWritePerms"
|
||||||
|
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
|
describe "HPath.IO.createSymlink" $ do
|
||||||
|
|
||||||
|
-- successes --
|
||||||
|
it "createSymlink, all fine" $ do
|
||||||
|
createSymlink' "newSymL" "alreadyExists/"
|
||||||
|
removeFileIfExists "newSymL"
|
||||||
|
|
||||||
|
-- posix failures --
|
||||||
|
it "createSymlink, can't write to destination directory" $
|
||||||
|
createSymlink' "noWritePerms/newDir" "lala"
|
||||||
|
`shouldThrow`
|
||||||
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "createSymlink, can't write to destination directory" $
|
||||||
|
createSymlink' "noPerms/newDir" "lala"
|
||||||
|
`shouldThrow`
|
||||||
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "createSymlink, destination file already exists" $
|
||||||
|
createSymlink' "alreadyExists" "lala"
|
||||||
|
`shouldThrow`
|
||||||
|
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||||
|
|
||||||
@@ -17,80 +17,99 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/deleteDirRecursiveSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "DeleteDirRecursiveSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "file"
|
||||||
|
createDir' "dir"
|
||||||
|
createRegularFile' "dir/.keep"
|
||||||
|
createSymlink' "dirSym" "dir/"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createRegularFile' "noPerms/.keep"
|
||||||
|
createDir' "noWritable"
|
||||||
|
createRegularFile' "noWritable/.keep"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
deleteFile' "file"
|
||||||
|
deleteFile' "dir/.keep"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteFile' "dirSym"
|
||||||
|
deleteFile' "noPerms/.keep"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteFile' "noWritable/.keep"
|
||||||
|
deleteDir' "noWritable"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.deleteDirRecursive" $ do
|
describe "HPath.IO.deleteDirRecursive" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "deleteDirRecursive, empty directory, all fine" $ do
|
it "deleteDirRecursive, empty directory, all fine" $ do
|
||||||
createDir' (specDir `ba` "testDir")
|
createDir' "testDir"
|
||||||
deleteDirRecursive' (specDir `ba` "testDir")
|
deleteDirRecursive' "testDir"
|
||||||
getSymbolicLinkStatus (specDir `ba` "testDir")
|
getSymbolicLinkStatus "testDir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "deleteDirRecursive, empty directory with null permissions, all fine" $ do
|
it "deleteDirRecursive, empty directory with null permissions, all fine" $ do
|
||||||
createDir' (specDir `ba` "noPerms/testDir")
|
createDir' "noPerms/testDir"
|
||||||
noPerms (specDir `ba` "noPerms/testDir")
|
noPerms "noPerms/testDir"
|
||||||
deleteDirRecursive' (specDir `ba` "noPerms/testDir")
|
deleteDirRecursive' "noPerms/testDir"
|
||||||
|
|
||||||
it "deleteDirRecursive, non-empty directory, all fine" $ do
|
it "deleteDirRecursive, non-empty directory, all fine" $ do
|
||||||
createDir' (specDir `ba` "nonEmpty")
|
createDir' "nonEmpty"
|
||||||
createDir' (specDir `ba` "nonEmpty/dir1")
|
createDir' "nonEmpty/dir1"
|
||||||
createDir' (specDir `ba` "nonEmpty/dir2")
|
createDir' "nonEmpty/dir2"
|
||||||
createDir' (specDir `ba` "nonEmpty/dir2/dir3")
|
createDir' "nonEmpty/dir2/dir3"
|
||||||
createRegularFile' (specDir `ba` "nonEmpty/file1")
|
createRegularFile' "nonEmpty/file1"
|
||||||
createRegularFile' (specDir `ba` "nonEmpty/dir1/file2")
|
createRegularFile' "nonEmpty/dir1/file2"
|
||||||
deleteDirRecursive' (specDir `ba` "nonEmpty")
|
deleteDirRecursive' "nonEmpty"
|
||||||
getSymbolicLinkStatus (specDir `ba` "nonEmpty")
|
getSymbolicLinkStatus "nonEmpty"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "deleteDirRecursive, can't open parent directory" $ do
|
it "deleteDirRecursive, can't open parent directory" $ do
|
||||||
createDir' (specDir `ba` "noPerms/foo")
|
createDir' "noPerms/foo"
|
||||||
noPerms (specDir `ba` "noPerms")
|
noPerms "noPerms"
|
||||||
(deleteDirRecursive' (specDir `ba` "noPerms/foo")
|
(deleteDirRecursive' "noPerms/foo")
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied))
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
>> normalDirPerms (specDir `ba` "noPerms")
|
normalDirPerms "noPerms"
|
||||||
>> deleteDir' (specDir `ba` "noPerms/foo")
|
deleteDir' "noPerms/foo"
|
||||||
|
|
||||||
it "deleteDirRecursive, can't write to parent directory" $ do
|
it "deleteDirRecursive, can't write to parent directory" $ do
|
||||||
createDir' (specDir `ba` "noWritable/foo")
|
createDir' "noWritable/foo"
|
||||||
noWritableDirPerms (specDir `ba` "noWritable")
|
noWritableDirPerms "noWritable"
|
||||||
(deleteDirRecursive' (specDir `ba` "noWritable/foo")
|
(deleteDirRecursive' "noWritable/foo")
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied))
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
normalDirPerms (specDir `ba` "noWritable")
|
normalDirPerms "noWritable"
|
||||||
deleteDir' (specDir `ba` "noWritable/foo")
|
deleteDir' "noWritable/foo"
|
||||||
|
|
||||||
it "deleteDirRecursive, wrong file type (symlink to directory)" $
|
it "deleteDirRecursive, wrong file type (symlink to directory)" $
|
||||||
deleteDirRecursive' (specDir `ba` "dirSym")
|
deleteDirRecursive' "dirSym"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "deleteDirRecursive, wrong file type (regular file)" $
|
it "deleteDirRecursive, wrong file type (regular file)" $
|
||||||
deleteDirRecursive' (specDir `ba` "file")
|
deleteDirRecursive' "file"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "deleteDirRecursive, directory does not exist" $
|
it "deleteDirRecursive, directory does not exist" $
|
||||||
deleteDirRecursive' (specDir `ba` "doesNotExist")
|
deleteDirRecursive' "doesNotExist"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
|
|||||||
@@ -17,78 +17,98 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
|
||||||
specDir = "test/HPath/IO/deleteDirSpec/"
|
|
||||||
|
|
||||||
specDir' :: String
|
upTmpDir :: IO ()
|
||||||
specDir' = toString specDir
|
upTmpDir = do
|
||||||
|
setTmpDir "DeleteDirSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "file"
|
||||||
|
createDir' "dir"
|
||||||
|
createRegularFile' "dir/.keep"
|
||||||
|
createSymlink' "dirSym" "dir/"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createRegularFile' "noPerms/.keep"
|
||||||
|
createDir' "noWritable"
|
||||||
|
createRegularFile' "noWritable/.keep"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
deleteFile' "file"
|
||||||
|
deleteFile' "dir/.keep"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteFile' "dirSym"
|
||||||
|
deleteFile' "noPerms/.keep"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteFile' "noWritable/.keep"
|
||||||
|
deleteDir' "noWritable"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.deleteDir" $ do
|
describe "HPath.IO.deleteDir" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "deleteDir, empty directory, all fine" $ do
|
it "deleteDir, empty directory, all fine" $ do
|
||||||
createDir' (specDir `ba` "testDir")
|
createDir' "testDir"
|
||||||
deleteDir' (specDir `ba` "testDir")
|
deleteDir' "testDir"
|
||||||
getSymbolicLinkStatus (specDir `ba` "testDir")
|
getSymbolicLinkStatus "testDir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "deleteDir, directory with null permissions, all fine" $ do
|
it "deleteDir, directory with null permissions, all fine" $ do
|
||||||
createDir' (specDir `ba` "noPerms/testDir")
|
createDir' "noPerms/testDir"
|
||||||
noPerms (specDir `ba` "noPerms/testDir")
|
noPerms "noPerms/testDir"
|
||||||
deleteDir' (specDir `ba` "noPerms/testDir")
|
deleteDir' "noPerms/testDir"
|
||||||
getSymbolicLinkStatus (specDir `ba` "testDir")
|
getSymbolicLinkStatus "testDir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "deleteDir, wrong file type (symlink to directory)" $
|
it "deleteDir, wrong file type (symlink to directory)" $
|
||||||
deleteDir' (specDir `ba` "dirSym")
|
deleteDir' "dirSym"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "deleteDir, wrong file type (regular file)" $
|
it "deleteDir, wrong file type (regular file)" $
|
||||||
deleteDir' (specDir `ba` "file")
|
deleteDir' "file"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "deleteDir, directory does not exist" $
|
it "deleteDir, directory does not exist" $
|
||||||
deleteDir' (specDir `ba` "doesNotExist")
|
deleteDir' "doesNotExist"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "deleteDir, directory not empty" $
|
it "deleteDir, directory not empty" $
|
||||||
deleteDir' (specDir `ba` "dir")
|
deleteDir' "dir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == UnsatisfiedConstraints)
|
(\e -> ioeGetErrorType e == UnsatisfiedConstraints)
|
||||||
|
|
||||||
it "deleteDir, can't open parent directory" $ do
|
it "deleteDir, can't open parent directory" $ do
|
||||||
createDir' (specDir `ba` "noPerms/foo")
|
createDir' "noPerms/foo"
|
||||||
noPerms (specDir `ba` "noPerms")
|
noPerms "noPerms"
|
||||||
(deleteDir' (specDir `ba` "noPerms/foo")
|
(deleteDir' "noPerms/foo")
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied))
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
>> normalDirPerms (specDir `ba` "noPerms")
|
normalDirPerms "noPerms"
|
||||||
>> deleteDir' (specDir `ba` "noPerms/foo")
|
deleteDir' "noPerms/foo"
|
||||||
|
|
||||||
it "deleteDir, can't write to parent directory, still fine" $ do
|
it "deleteDir, can't write to parent directory, still fine" $ do
|
||||||
createDir' (specDir `ba` "noWritable/foo")
|
createDir' "noWritable/foo"
|
||||||
noWritableDirPerms (specDir `ba` "noWritable")
|
noWritableDirPerms "noWritable"
|
||||||
(deleteDir' (specDir `ba` "noWritable/foo")
|
(deleteDir' "noWritable/foo")
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied))
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
normalDirPerms (specDir `ba` "noWritable")
|
normalDirPerms "noWritable"
|
||||||
deleteDir' (specDir `ba` "noWritable/foo")
|
deleteDir' "noWritable/foo"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module HPath.IO.DeleteFileSpec where
|
|||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import HPath.IO
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@@ -17,53 +18,67 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
upTmpDir :: IO ()
|
||||||
ba = BS.append
|
upTmpDir = do
|
||||||
|
setTmpDir "DeleteFileSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
|
||||||
specDir = "test/HPath/IO/deleteFileSpec/"
|
|
||||||
|
|
||||||
specDir' :: String
|
setupFiles :: IO ()
|
||||||
specDir' = toString specDir
|
setupFiles = do
|
||||||
|
createRegularFile' "foo"
|
||||||
|
createSymlink' "syml" "foo"
|
||||||
|
createDir' "dir"
|
||||||
|
createDir' "noPerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
deleteFile' "foo"
|
||||||
|
deleteFile' "syml"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.deleteFile" $ do
|
describe "HPath.IO.deleteFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "deleteFile, regular file, all fine" $ do
|
it "deleteFile, regular file, all fine" $ do
|
||||||
createRegularFile' (specDir `ba` "testFile")
|
createRegularFile' "testFile"
|
||||||
deleteFile' (specDir `ba` "testFile")
|
deleteFile' "testFile"
|
||||||
getSymbolicLinkStatus (specDir `ba` "testFile")
|
getSymbolicLinkStatus "testFile"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "deleteFile, symlink, all fine" $ do
|
it "deleteFile, symlink, all fine" $ do
|
||||||
recreateSymlink' (specDir `ba` "syml")
|
recreateSymlink' "syml"
|
||||||
(specDir `ba` "testFile")
|
"testFile"
|
||||||
deleteFile' (specDir `ba` "testFile")
|
Strict
|
||||||
getSymbolicLinkStatus (specDir `ba` "testFile")
|
deleteFile' "testFile"
|
||||||
|
getSymbolicLinkStatus "testFile"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "deleteFile, wrong file type (directory)" $
|
it "deleteFile, wrong file type (directory)" $
|
||||||
deleteFile' (specDir `ba` "dir")
|
deleteFile' "dir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "deleteFile, file does not exist" $
|
it "deleteFile, file does not exist" $
|
||||||
deleteFile' (specDir `ba` "doesNotExist")
|
deleteFile' "doesNotExist"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "deleteFile, can't read directory" $
|
it "deleteFile, can't read directory" $
|
||||||
deleteFile' (specDir `ba` "noPerms/blah")
|
deleteFile' "noPerms/blah"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
|||||||
@@ -3,87 +3,95 @@
|
|||||||
module HPath.IO.GetDirsFilesSpec where
|
module HPath.IO.GetDirsFilesSpec where
|
||||||
|
|
||||||
|
|
||||||
import Control.Applicative
|
|
||||||
(
|
|
||||||
(<$>)
|
|
||||||
)
|
|
||||||
import Data.List
|
import Data.List
|
||||||
(
|
(
|
||||||
sort
|
sort
|
||||||
)
|
)
|
||||||
import Data.Maybe
|
|
||||||
(
|
|
||||||
fromJust
|
|
||||||
)
|
|
||||||
import qualified HPath as P
|
import qualified HPath as P
|
||||||
|
import HPath.IO
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
)
|
)
|
||||||
import System.Posix.Env.ByteString
|
|
||||||
(
|
|
||||||
getEnv
|
|
||||||
)
|
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
(
|
(
|
||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
upTmpDir :: IO ()
|
||||||
ba = BS.append
|
upTmpDir = do
|
||||||
|
setTmpDir "GetDirsFilesSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
|
||||||
specDir = "test/HPath/IO/getDirsFilesSpec/"
|
|
||||||
|
|
||||||
specDir' :: String
|
setupFiles :: IO ()
|
||||||
specDir' = toString specDir
|
setupFiles = do
|
||||||
|
createRegularFile' "file"
|
||||||
|
createRegularFile' "Lala"
|
||||||
|
createRegularFile' ".hidden"
|
||||||
|
createSymlink' "syml" "Lala"
|
||||||
|
createDir' "dir"
|
||||||
|
createSymlink' "dirsym" "dir"
|
||||||
|
createDir' "noPerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
deleteFile' "file"
|
||||||
|
deleteFile' "Lala"
|
||||||
|
deleteFile' ".hidden"
|
||||||
|
deleteFile' "syml"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteFile' "dirsym"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.getDirsFiles" $ do
|
describe "HPath.IO.getDirsFiles" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "getDirsFiles, all fine" $ do
|
it "getDirsFiles, all fine" $
|
||||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
withRawTmpDir $ \p -> do
|
||||||
expectedFiles <- mapM P.parseRel [(specDir `ba ` ".hidden")
|
expectedFiles <- mapM P.parseRel [".hidden"
|
||||||
,(specDir `ba ` "Lala")
|
,"Lala"
|
||||||
,(specDir `ba ` "dir")
|
,"dir"
|
||||||
,(specDir `ba ` "dirsym")
|
,"dirsym"
|
||||||
,(specDir `ba ` "file")
|
,"file"
|
||||||
,(specDir `ba ` "noPerms")
|
,"noPerms"
|
||||||
,(specDir `ba ` "syml")]
|
,"syml"]
|
||||||
(fmap sort $ getDirsFiles' specDir)
|
(fmap sort $ getDirsFiles p)
|
||||||
`shouldReturn` fmap (pwd P.</>) expectedFiles
|
`shouldReturn` fmap (p P.</>) expectedFiles
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "getDirsFiles, nonexistent directory" $
|
it "getDirsFiles, nonexistent directory" $
|
||||||
getDirsFiles' (specDir `ba ` "nothingHere")
|
getDirsFiles' "nothingHere"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "getDirsFiles, wrong file type (file)" $
|
it "getDirsFiles, wrong file type (file)" $
|
||||||
getDirsFiles' (specDir `ba ` "file")
|
getDirsFiles' "file"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "getDirsFiles, wrong file type (symlink to file)" $
|
it "getDirsFiles, wrong file type (symlink to file)" $
|
||||||
getDirsFiles' (specDir `ba ` "syml")
|
getDirsFiles' "syml"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||||
|
|
||||||
it "getDirsFiles, wrong file type (symlink to dir)" $
|
it "getDirsFiles, wrong file type (symlink to dir)" $
|
||||||
getDirsFiles' (specDir `ba ` "dirsym")
|
getDirsFiles' "dirsym"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||||
|
|
||||||
it "getDirsFiles, can't open directory" $
|
it "getDirsFiles, can't open directory" $
|
||||||
getDirsFiles' (specDir `ba ` "noPerms")
|
getDirsFiles' "noPerms"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
|||||||
@@ -14,57 +14,75 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/getFileTypeSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "GetFileTypeSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "regularfile"
|
||||||
|
createSymlink' "symlink" "regularfile"
|
||||||
|
createSymlink' "brokenSymlink" "broken"
|
||||||
|
createDir' "directory"
|
||||||
|
createSymlink' "symlinkD" "directory"
|
||||||
|
createDir' "noPerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
deleteFile' "regularfile"
|
||||||
|
deleteFile' "symlink"
|
||||||
|
deleteFile' "brokenSymlink"
|
||||||
|
deleteDir' "directory"
|
||||||
|
deleteFile' "symlinkD"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.getFileType" $ do
|
describe "HPath.IO.getFileType" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "getFileType, regular file" $
|
it "getFileType, regular file" $
|
||||||
getFileType' (specDir `ba` "regularfile")
|
getFileType' "regularfile"
|
||||||
`shouldReturn` RegularFile
|
`shouldReturn` RegularFile
|
||||||
|
|
||||||
it "getFileType, directory" $
|
it "getFileType, directory" $
|
||||||
getFileType' (specDir `ba` "directory")
|
getFileType' "directory"
|
||||||
`shouldReturn` Directory
|
`shouldReturn` Directory
|
||||||
|
|
||||||
it "getFileType, directory with null permissions" $
|
it "getFileType, directory with null permissions" $
|
||||||
getFileType' (specDir `ba` "noPerms")
|
getFileType' "noPerms"
|
||||||
`shouldReturn` Directory
|
`shouldReturn` Directory
|
||||||
|
|
||||||
it "getFileType, symlink to file" $
|
it "getFileType, symlink to file" $
|
||||||
getFileType' (specDir `ba` "symlink")
|
getFileType' "symlink"
|
||||||
`shouldReturn` SymbolicLink
|
`shouldReturn` SymbolicLink
|
||||||
|
|
||||||
it "getFileType, symlink to directory" $
|
it "getFileType, symlink to directory" $
|
||||||
getFileType' (specDir `ba` "symlinkD")
|
getFileType' "symlinkD"
|
||||||
`shouldReturn` SymbolicLink
|
`shouldReturn` SymbolicLink
|
||||||
|
|
||||||
it "getFileType, broken symlink" $
|
it "getFileType, broken symlink" $
|
||||||
getFileType' (specDir `ba` "brokenSymlink")
|
getFileType' "brokenSymlink"
|
||||||
`shouldReturn` SymbolicLink
|
`shouldReturn` SymbolicLink
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "getFileType, file does not exist" $
|
it "getFileType, file does not exist" $
|
||||||
getFileType' (specDir `ba` "nothingHere")
|
getFileType' "nothingHere"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "getFileType, can't open directory" $
|
it "getFileType, can't open directory" $
|
||||||
getFileType' (specDir `ba` "noPerms/forz")
|
getFileType' "noPerms/forz"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module HPath.IO.MoveFileOverwriteSpec where
|
|||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import HPath.IO
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@@ -14,80 +15,112 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/moveFileOverwriteSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "MoveFileOverwriteSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "myFile"
|
||||||
|
createSymlink' "myFileL" "myFile"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createDir' "dir"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createDir' "noWritePerm"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerm"
|
||||||
|
writeFile' "myFile" "Blahfaselgagaga"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerm"
|
||||||
|
deleteFile' "myFile"
|
||||||
|
deleteFile' "myFileL"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "noWritePerm"
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.moveFileOverwrite" $ do
|
describe "HPath.IO.moveFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "moveFileOverwrite, all fine" $
|
it "moveFile (Overwrite), all fine" $
|
||||||
moveFileOverwrite' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Overwrite
|
||||||
|
|
||||||
it "moveFileOverwrite, all fine" $
|
it "moveFile (Overwrite), all fine" $
|
||||||
moveFileOverwrite' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "dir/movedFile")
|
"dir/movedFile"
|
||||||
|
Overwrite
|
||||||
|
|
||||||
it "moveFileOverwrite, all fine on symlink" $
|
it "moveFile (Overwrite), all fine on symlink" $
|
||||||
moveFileOverwrite' (specDir `ba` "myFileL")
|
moveFile' "myFileL"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Overwrite
|
||||||
|
|
||||||
it "moveFileOverwrite, all fine on directory" $
|
it "moveFile (Overwrite), all fine on directory" $
|
||||||
moveFileOverwrite' (specDir `ba` "dir")
|
moveFile' "dir"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Overwrite
|
||||||
|
|
||||||
it "moveFileOverwrite, destination file already exists" $
|
it "moveFile (Overwrite), destination file already exists" $
|
||||||
moveFileOverwrite' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "alreadyExists")
|
"alreadyExists"
|
||||||
|
Overwrite
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "moveFileOverwrite, source file does not exist" $
|
it "moveFile (Overwrite), source file does not exist" $
|
||||||
moveFileOverwrite' (specDir `ba` "fileDoesNotExist")
|
moveFile' "fileDoesNotExist"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "moveFileOverwrite, can't write to destination directory" $
|
it "moveFile (Overwrite), can't write to destination directory" $
|
||||||
moveFileOverwrite' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "noWritePerm/movedFile")
|
"noWritePerm/movedFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "moveFileOverwrite, can't open destination directory" $
|
it "moveFile (Overwrite), can't open destination directory" $
|
||||||
moveFileOverwrite' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "noPerms/movedFile")
|
"noPerms/movedFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "moveFileOverwrite, can't open source directory" $
|
it "moveFile (Overwrite), can't open source directory" $
|
||||||
moveFileOverwrite' (specDir `ba` "noPerms/myFile")
|
moveFile' "noPerms/myFile"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
-- custom failures --
|
-- custom failures --
|
||||||
it "moveFileOverwrite, move from file to dir" $
|
|
||||||
moveFileOverwrite' (specDir `ba` "myFile")
|
it "moveFile (Overwrite), move from file to dir" $
|
||||||
(specDir `ba` "alreadyExistsD")
|
moveFile' "myFile"
|
||||||
|
"alreadyExistsD"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isDirDoesExist
|
isDirDoesExist
|
||||||
|
|
||||||
it "moveFileOverwrite, source and dest are same file" $
|
it "moveFile (Overwrite), source and dest are same file" $
|
||||||
moveFileOverwrite' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "myFile")
|
"myFile"
|
||||||
|
Overwrite
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isSameFile
|
isSameFile
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module HPath.IO.MoveFileSpec where
|
|||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import HPath.IO
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@@ -14,82 +15,115 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/moveFileSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "MoveFileSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "myFile"
|
||||||
|
createSymlink' "myFileL" "myFile"
|
||||||
|
createRegularFile' "alreadyExists"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createDir' "dir"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createDir' "noWritePerm"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerm"
|
||||||
|
writeFile' "myFile" "Blahfaselgagaga"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerm"
|
||||||
|
deleteFile' "myFile"
|
||||||
|
deleteFile' "myFileL"
|
||||||
|
deleteFile' "alreadyExists"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "noWritePerm"
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.moveFile" $ do
|
describe "HPath.IO.moveFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "moveFile, all fine" $
|
it "moveFile (Strict), all fine" $
|
||||||
moveFile' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Strict
|
||||||
|
|
||||||
it "moveFile, all fine" $
|
it "moveFile (Strict), all fine" $
|
||||||
moveFile' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "dir/movedFile")
|
"dir/movedFile"
|
||||||
|
Strict
|
||||||
|
|
||||||
it "moveFile, all fine on symlink" $
|
it "moveFile (Strict), all fine on symlink" $
|
||||||
moveFile' (specDir `ba` "myFileL")
|
moveFile' "myFileL"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Strict
|
||||||
|
|
||||||
it "moveFile, all fine on directory" $
|
it "moveFile (Strict), all fine on directory" $
|
||||||
moveFile' (specDir `ba` "dir")
|
moveFile' "dir"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Strict
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "moveFile, source file does not exist" $
|
it "moveFile (Strict), source file does not exist" $
|
||||||
moveFile' (specDir `ba` "fileDoesNotExist")
|
moveFile' "fileDoesNotExist"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "moveFile, can't write to destination directory" $
|
it "moveFile (Strict), can't write to destination directory" $
|
||||||
moveFile' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "noWritePerm/movedFile")
|
"noWritePerm/movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "moveFile, can't open destination directory" $
|
it "moveFile (Strict), can't open destination directory" $
|
||||||
moveFile' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "noPerms/movedFile")
|
"noPerms/movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "moveFile, can't open source directory" $
|
it "moveFile (Strict), can't open source directory" $
|
||||||
moveFile' (specDir `ba` "noPerms/myFile")
|
moveFile' "noPerms/myFile"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
-- custom failures --
|
-- custom failures --
|
||||||
it "moveFile, destination file already exists" $
|
it "moveFile (Strict), destination file already exists" $
|
||||||
moveFile' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "alreadyExists")
|
"alreadyExists"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isFileDoesExist
|
isFileDoesExist
|
||||||
|
|
||||||
it "moveFile, move from file to dir" $
|
it "moveFile (Strict), move from file to dir" $
|
||||||
moveFile' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "alreadyExistsD")
|
"alreadyExistsD"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isDirDoesExist
|
isDirDoesExist
|
||||||
|
|
||||||
it "moveFile, source and dest are same file" $
|
it "moveFile (Strict), source and dest are same file" $
|
||||||
moveFile' (specDir `ba` "myFile")
|
moveFile' "myFile"
|
||||||
(specDir `ba` "myFile")
|
"myFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isSameFile
|
isSameFile
|
||||||
|
|
||||||
|
|||||||
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
|
module HPath.IO.RecreateSymlinkSpec where
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import HPath.IO
|
||||||
import HPath.IO.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@@ -14,82 +17,114 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/recreateSymlinkSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "RecreateSymlinkSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "myFile"
|
||||||
|
createSymlink' "myFileL" "myFile"
|
||||||
|
createRegularFile' "alreadyExists"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createDir' "dir"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createDir' "noWritePerm"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerm"
|
||||||
|
writeFile' "myFile" "Blahfaselgagaga"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerm"
|
||||||
|
deleteFile' "myFile"
|
||||||
|
deleteFile' "myFileL"
|
||||||
|
deleteFile' "alreadyExists"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "noWritePerm"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.recreateSymlink" $ do
|
describe "HPath.IO.recreateSymlink" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "recreateSymLink, all fine" $ do
|
it "recreateSymLink (Strict), all fine" $ do
|
||||||
recreateSymlink' (specDir `ba` "myFileL")
|
recreateSymlink' "myFileL"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
removeFileIfExists (specDir `ba` "movedFile")
|
Strict
|
||||||
|
removeFileIfExists "movedFile"
|
||||||
|
|
||||||
it "recreateSymLink, all fine" $ do
|
it "recreateSymLink (Strict), all fine" $ do
|
||||||
recreateSymlink' (specDir `ba` "myFileL")
|
recreateSymlink' "myFileL"
|
||||||
(specDir `ba` "dir/movedFile")
|
"dir/movedFile"
|
||||||
removeFileIfExists (specDir `ba` "dir/movedFile")
|
Strict
|
||||||
|
removeFileIfExists "dir/movedFile"
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "recreateSymLink, wrong input type (file)" $
|
it "recreateSymLink (Strict), wrong input type (file)" $
|
||||||
recreateSymlink' (specDir `ba` "myFile")
|
recreateSymlink' "myFile"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||||
|
|
||||||
it "recreateSymLink, wrong input type (directory)" $
|
it "recreateSymLink (Strict), wrong input type (directory)" $
|
||||||
recreateSymlink' (specDir `ba` "dir")
|
recreateSymlink' "dir"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||||
|
|
||||||
it "recreateSymLink, can't write to destination directory" $
|
it "recreateSymLink (Strict), can't write to destination directory" $
|
||||||
recreateSymlink' (specDir `ba` "myFileL")
|
recreateSymlink' "myFileL"
|
||||||
(specDir `ba` "noWritePerm/movedFile")
|
"noWritePerm/movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "recreateSymLink, can't open destination directory" $
|
it "recreateSymLink (Strict), can't open destination directory" $
|
||||||
recreateSymlink' (specDir `ba` "myFileL")
|
recreateSymlink' "myFileL"
|
||||||
(specDir `ba` "noPerms/movedFile")
|
"noPerms/movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "recreateSymLink, can't open source directory" $
|
it "recreateSymLink (Strict), can't open source directory" $
|
||||||
recreateSymlink' (specDir `ba` "noPerms/myFileL")
|
recreateSymlink' "noPerms/myFileL"
|
||||||
(specDir `ba` "movedFile")
|
"movedFile"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "recreateSymLink, destination file already exists" $
|
it "recreateSymLink (Strict), destination file already exists" $
|
||||||
recreateSymlink' (specDir `ba` "myFileL")
|
recreateSymlink' "myFileL"
|
||||||
(specDir `ba` "alreadyExists")
|
"alreadyExists"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||||
|
|
||||||
it "recreateSymLink, destination already exists and is a dir" $
|
it "recreateSymLink (Strict), destination already exists and is a dir" $
|
||||||
recreateSymlink' (specDir `ba` "myFileL")
|
recreateSymlink' "myFileL"
|
||||||
(specDir `ba` "alreadyExistsD")
|
"alreadyExistsD"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||||
|
|
||||||
-- custom failures --
|
-- custom failures --
|
||||||
it "recreateSymLink, source and destination are the same file" $
|
it "recreateSymLink (Strict), source and destination are the same file" $
|
||||||
recreateSymlink' (specDir `ba` "myFileL")
|
recreateSymlink' "myFileL"
|
||||||
(specDir `ba` "myFileL")
|
"myFileL"
|
||||||
|
Strict
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isSameFile
|
isSameFile
|
||||||
|
|
||||||
|
|||||||
@@ -14,82 +14,104 @@ import GHC.IO.Exception
|
|||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
import Utils
|
import Utils
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8 (toString)
|
|
||||||
|
|
||||||
|
|
||||||
ba :: BS.ByteString -> BS.ByteString -> BS.ByteString
|
|
||||||
ba = BS.append
|
|
||||||
|
|
||||||
specDir :: BS.ByteString
|
upTmpDir :: IO ()
|
||||||
specDir = "test/HPath/IO/renameFileSpec/"
|
upTmpDir = do
|
||||||
|
setTmpDir "RenameFileSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
specDir' :: String
|
|
||||||
specDir' = toString specDir
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "myFile"
|
||||||
|
createSymlink' "myFileL" "myFile"
|
||||||
|
createRegularFile' "alreadyExists"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createDir' "dir"
|
||||||
|
createDir' "noPerms"
|
||||||
|
createDir' "noWritePerm"
|
||||||
|
noPerms "noPerms"
|
||||||
|
noWritableDirPerms "noWritePerm"
|
||||||
|
writeFile' "myFile" "Blahfaselgagaga"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
normalDirPerms "noPerms"
|
||||||
|
normalDirPerms "noWritePerm"
|
||||||
|
deleteFile' "myFile"
|
||||||
|
deleteFile' "myFileL"
|
||||||
|
deleteFile' "alreadyExists"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
deleteDir' "dir"
|
||||||
|
deleteDir' "noPerms"
|
||||||
|
deleteDir' "noWritePerm"
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec =
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "HPath.IO.renameFile" $ do
|
describe "HPath.IO.renameFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "renameFile, all fine" $
|
it "renameFile, all fine" $
|
||||||
renameFile' (specDir `ba` "myFile")
|
renameFile' "myFile"
|
||||||
(specDir `ba` "renamedFile")
|
"renamedFile"
|
||||||
|
|
||||||
it "renameFile, all fine" $
|
it "renameFile, all fine" $
|
||||||
renameFile' (specDir `ba` "myFile")
|
renameFile' "myFile"
|
||||||
(specDir `ba` "dir/renamedFile")
|
"dir/renamedFile"
|
||||||
|
|
||||||
it "renameFile, all fine on symlink" $
|
it "renameFile, all fine on symlink" $
|
||||||
renameFile' (specDir `ba` "myFileL")
|
renameFile' "myFileL"
|
||||||
(specDir `ba` "renamedFile")
|
"renamedFile"
|
||||||
|
|
||||||
it "renameFile, all fine on directory" $
|
it "renameFile, all fine on directory" $
|
||||||
renameFile' (specDir `ba` "dir")
|
renameFile' "dir"
|
||||||
(specDir `ba` "renamedFile")
|
"renamedFile"
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "renameFile, source file does not exist" $
|
it "renameFile, source file does not exist" $
|
||||||
renameFile' (specDir `ba` "fileDoesNotExist")
|
renameFile' "fileDoesNotExist"
|
||||||
(specDir `ba` "renamedFile")
|
"renamedFile"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||||
|
|
||||||
it "renameFile, can't write to output directory" $
|
it "renameFile, can't write to output directory" $
|
||||||
renameFile' (specDir `ba` "myFile")
|
renameFile' "myFile"
|
||||||
(specDir `ba` "noWritePerm/renamedFile")
|
"noWritePerm/renamedFile"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "renameFile, can't open output directory" $
|
it "renameFile, can't open output directory" $
|
||||||
renameFile' (specDir `ba` "myFile")
|
renameFile' "myFile"
|
||||||
(specDir `ba` "noPerms/renamedFile")
|
"noPerms/renamedFile"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
it "renameFile, can't open source directory" $
|
it "renameFile, can't open source directory" $
|
||||||
renameFile' (specDir `ba` "noPerms/myFile")
|
renameFile' "noPerms/myFile"
|
||||||
(specDir `ba` "renamedFile")
|
"renamedFile"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
-- custom failures --
|
-- custom failures --
|
||||||
it "renameFile, destination file already exists" $
|
it "renameFile, destination file already exists" $
|
||||||
renameFile' (specDir `ba` "myFile")
|
renameFile' "myFile"
|
||||||
(specDir `ba` "alreadyExists")
|
"alreadyExists"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isFileDoesExist
|
isFileDoesExist
|
||||||
|
|
||||||
it "renameFile, move from file to dir" $
|
it "renameFile, move from file to dir" $
|
||||||
renameFile' (specDir `ba` "myFile")
|
renameFile' "myFile"
|
||||||
(specDir `ba` "alreadyExistsD")
|
"alreadyExistsD"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isDirDoesExist
|
isDirDoesExist
|
||||||
|
|
||||||
it "renameFile, source and dest are same file" $
|
it "renameFile, source and dest are same file" $
|
||||||
renameFile' (specDir `ba` "myFile")
|
renameFile' "myFile"
|
||||||
(specDir `ba` "myFile")
|
"myFile"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
isSameFile
|
isSameFile
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
nothing
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dir
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
file
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
sda
|
|
||||||
|
|
||||||
!!1
|
|
||||||
sda
|
|
||||||
|
|
||||||
|
|
||||||
11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
das
|
|
||||||
sda
|
|
||||||
sda
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
sda
|
|
||||||
|
|
||||||
!!1
|
|
||||||
sda
|
|
||||||
|
|
||||||
|
|
||||||
11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
das
|
|
||||||
sda
|
|
||||||
sda
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
sda
|
|
||||||
|
|
||||||
!!1
|
|
||||||
sda
|
|
||||||
|
|
||||||
|
|
||||||
11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
das
|
|
||||||
sda
|
|
||||||
sda
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
inputDir/
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
sda
|
|
||||||
|
|
||||||
!!1
|
|
||||||
sda
|
|
||||||
|
|
||||||
|
|
||||||
11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
das
|
|
||||||
sda
|
|
||||||
sda
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
sda
|
|
||||||
|
|
||||||
!!1
|
|
||||||
sda
|
|
||||||
|
|
||||||
|
|
||||||
11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
dadasasddas
|
|
||||||
das
|
|
||||||
sda
|
|
||||||
sda
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
inputDir/
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
adaöölsdaöl
|
|
||||||
dsalö
|
|
||||||
ölsda
|
|
||||||
ääödsf
|
|
||||||
äsdfä
|
|
||||||
öä453
|
|
||||||
öä
|
|
||||||
435
|
|
||||||
ä45343
|
|
||||||
5
|
|
||||||
453
|
|
||||||
453453453
|
|
||||||
das
|
|
||||||
asd
|
|
||||||
das
|
|
||||||
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
abc
|
|
||||||
def
|
|
||||||
|
|
||||||
dsadasdsa
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
inputFile
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
abc
|
|
||||||
def
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
inputFile
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dir
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dir
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
foo
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user